roojs-core.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244     
245         
246         skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249             
250             // if parent was disabled, then do not try and create the children..
251             if(!this[cntr](true)){
252                 tree.items = [];
253                 return tree;
254             }
255            
256             cn = Roo.factory(tree);
257            
258             cn.parentType = this.xtype; //??
259             cn.parentId = this.id;
260             
261             var build_from_html =  Roo.XComponent.build_from_html;
262             
263             
264             // does the container contain child eleemnts with 'xtype' attributes.
265             // that match this xtype..
266             // note - when we render we create these as well..
267             // so we should check to see if body has xtype set.
268             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
269                
270                 var self_cntr_el = Roo.get(this[cntr](false));
271                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
272                 if (echild) { 
273                     //Roo.log(Roo.XComponent.build_from_html);
274                     //Roo.log("got echild:");
275                     //Roo.log(echild);
276                 }
277                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
278                 // and are not displayed -this causes this to use up the wrong element when matching.
279                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
280                 
281                 
282                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
284                   
285                   
286                   
287                     cn.el = echild;
288                   //  Roo.log("GOT");
289                     //echild.dom.removeAttribute('xtype');
290                 } else {
291                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
292                     Roo.debug && Roo.log(self_cntr_el);
293                     Roo.debug && Roo.log(echild);
294                     Roo.debug && Roo.log(cn);
295                 }
296             }
297            
298             
299            
300             // if object has flexy:if - then it may or may not be rendered.
301             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
302                 // skip a flexy if element.
303                 Roo.debug && Roo.log('skipping render');
304                 Roo.debug && Roo.log(tree);
305                 if (!cn.el) {
306                     Roo.debug && Roo.log('skipping all children');
307                     skip_children = true;
308                 }
309                 
310              } else {
311                  
312                 // actually if flexy:foreach is found, we really want to create 
313                 // multiple copies here...
314                 //Roo.log('render');
315                 //Roo.log(this[cntr]());
316                 // some elements do not have render methods.. like the layouts...
317                 /*
318                 if(this[cntr](true) === false){
319                     cn.items = [];
320                     return cn;
321                 }
322                 */
323                 cn.render && cn.render(this[cntr](true));
324                 
325              }
326             // then add the element..
327         }
328          
329         // handle the kids..
330         
331         var nitems = [];
332         /*
333         if (typeof (tree.menu) != 'undefined') {
334             tree.menu.parentType = cn.xtype;
335             tree.menu.triggerEl = cn.el;
336             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
337             
338         }
339         */
340         if (!tree.items || !tree.items.length) {
341             cn.items = nitems;
342             //Roo.log(["no children", this]);
343             
344             return cn;
345         }
346          
347         var items = tree.items;
348         delete tree.items;
349         
350         //Roo.log(items.length);
351             // add the items..
352         if (!skip_children) {    
353             for(var i =0;i < items.length;i++) {
354               //  Roo.log(['add child', items[i]]);
355                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356             }
357         }
358         
359         cn.items = nitems;
360         
361         //Roo.log("fire childrenrendered");
362         
363         cn.fireEvent('childrenrendered', this);
364         
365         return cn;
366     },
367     /**
368      * Show a component - removes 'hidden' class
369      */
370     show : function()
371     {
372         if (this.el) {
373             this.el.removeClass('hidden');
374         }
375     },
376     /**
377      * Hide a component - adds 'hidden' class
378      */
379     hide: function()
380     {
381         if (this.el && !this.el.hasClass('hidden')) {
382             this.el.addClass('hidden');
383         }
384     }
385 });
386
387  /*
388  * - LGPL
389  *
390  * Body
391  *
392  */
393
394 /**
395  * @class Roo.bootstrap.Body
396  * @extends Roo.bootstrap.Component
397  * Bootstrap Body class
398  *
399  * @constructor
400  * Create a new body
401  * @param {Object} config The config object
402  */
403
404 Roo.bootstrap.Body = function(config){
405
406     config = config || {};
407
408     Roo.bootstrap.Body.superclass.constructor.call(this, config);
409     this.el = Roo.get(config.el ? config.el : document.body );
410     if (this.cls && this.cls.length) {
411         Roo.get(document.body).addClass(this.cls);
412     }
413 };
414
415 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
416
417     is_body : true,// just to make sure it's constructed?
418
419         autoCreate : {
420         cls: 'container'
421     },
422     onRender : function(ct, position)
423     {
424        /* Roo.log("Roo.bootstrap.Body - onRender");
425         if (this.cls && this.cls.length) {
426             Roo.get(document.body).addClass(this.cls);
427         }
428         // style??? xttr???
429         */
430     }
431
432
433
434
435 });
436 /*
437  * - LGPL
438  *
439  * button group
440  * 
441  */
442
443
444 /**
445  * @class Roo.bootstrap.ButtonGroup
446  * @extends Roo.bootstrap.Component
447  * Bootstrap ButtonGroup class
448  * @cfg {String} size lg | sm | xs (default empty normal)
449  * @cfg {String} align vertical | justified  (default none)
450  * @cfg {String} direction up | down (default down)
451  * @cfg {Boolean} toolbar false | true
452  * @cfg {Boolean} btn true | false
453  * 
454  * 
455  * @constructor
456  * Create a new Input
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.ButtonGroup = function(config){
461     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
462 };
463
464 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
465     
466     size: '',
467     align: '',
468     direction: '',
469     toolbar: false,
470     btn: true,
471
472     getAutoCreate : function(){
473         var cfg = {
474             cls: 'btn-group',
475             html : null
476         };
477         
478         cfg.html = this.html || cfg.html;
479         
480         if (this.toolbar) {
481             cfg = {
482                 cls: 'btn-toolbar',
483                 html: null
484             };
485             
486             return cfg;
487         }
488         
489         if (['vertical','justified'].indexOf(this.align)!==-1) {
490             cfg.cls = 'btn-group-' + this.align;
491             
492             if (this.align == 'justified') {
493                 console.log(this.items);
494             }
495         }
496         
497         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
498             cfg.cls += ' btn-group-' + this.size;
499         }
500         
501         if (this.direction == 'up') {
502             cfg.cls += ' dropup' ;
503         }
504         
505         return cfg;
506     }
507    
508 });
509
510  /*
511  * - LGPL
512  *
513  * button
514  * 
515  */
516
517 /**
518  * @class Roo.bootstrap.Button
519  * @extends Roo.bootstrap.Component
520  * Bootstrap Button class
521  * @cfg {String} html The button content
522  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
523  * @cfg {String} size ( lg | sm | xs)
524  * @cfg {String} tag ( a | input | submit)
525  * @cfg {String} href empty or href
526  * @cfg {Boolean} disabled default false;
527  * @cfg {Boolean} isClose default false;
528  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
529  * @cfg {String} badge text for badge
530  * @cfg {String} theme default 
531  * @cfg {Boolean} inverse 
532  * @cfg {Boolean} toggle 
533  * @cfg {String} ontext text for on toggle state
534  * @cfg {String} offtext text for off toggle state
535  * @cfg {Boolean} defaulton 
536  * @cfg {Boolean} preventDefault  default true
537  * @cfg {Boolean} removeClass remove the standard class..
538  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
539  * 
540  * @constructor
541  * Create a new button
542  * @param {Object} config The config object
543  */
544
545
546 Roo.bootstrap.Button = function(config){
547     Roo.bootstrap.Button.superclass.constructor.call(this, config);
548     this.weightClass = ["btn-default", 
549                        "btn-primary", 
550                        "btn-success", 
551                        "btn-info", 
552                        "btn-warning",
553                        "btn-danger",
554                        "btn-link"
555                       ],  
556     this.addEvents({
557         // raw events
558         /**
559          * @event click
560          * When a butotn is pressed
561          * @param {Roo.bootstrap.Button} this
562          * @param {Roo.EventObject} e
563          */
564         "click" : true,
565          /**
566          * @event toggle
567          * After the button has been toggles
568          * @param {Roo.EventObject} e
569          * @param {boolean} pressed (also available as button.pressed)
570          */
571         "toggle" : true
572     });
573 };
574
575 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
576     html: false,
577     active: false,
578     weight: '',
579     size: '',
580     tag: 'button',
581     href: '',
582     disabled: false,
583     isClose: false,
584     glyphicon: '',
585     badge: '',
586     theme: 'default',
587     inverse: false,
588     
589     toggle: false,
590     ontext: 'ON',
591     offtext: 'OFF',
592     defaulton: true,
593     preventDefault: true,
594     removeClass: false,
595     name: false,
596     target: false,
597     
598     
599     pressed : null,
600      
601     
602     getAutoCreate : function(){
603         
604         var cfg = {
605             tag : 'button',
606             cls : 'roo-button',
607             html: ''
608         };
609         
610         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
611             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
612             this.tag = 'button';
613         } else {
614             cfg.tag = this.tag;
615         }
616         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
617         
618         if (this.toggle == true) {
619             cfg={
620                 tag: 'div',
621                 cls: 'slider-frame roo-button',
622                 cn: [
623                     {
624                         tag: 'span',
625                         'data-on-text':'ON',
626                         'data-off-text':'OFF',
627                         cls: 'slider-button',
628                         html: this.offtext
629                     }
630                 ]
631             };
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 cfg.cls += ' '+this.weight;
635             }
636             
637             return cfg;
638         }
639         
640         if (this.isClose) {
641             cfg.cls += ' close';
642             
643             cfg["aria-hidden"] = true;
644             
645             cfg.html = "&times;";
646             
647             return cfg;
648         }
649         
650          
651         if (this.theme==='default') {
652             cfg.cls = 'btn roo-button';
653             
654             //if (this.parentType != 'Navbar') {
655             this.weight = this.weight.length ?  this.weight : 'default';
656             //}
657             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
658                 
659                 cfg.cls += ' btn-' + this.weight;
660             }
661         } else if (this.theme==='glow') {
662             
663             cfg.tag = 'a';
664             cfg.cls = 'btn-glow roo-button';
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 
668                 cfg.cls += ' ' + this.weight;
669             }
670         }
671    
672         
673         if (this.inverse) {
674             this.cls += ' inverse';
675         }
676         
677         
678         if (this.active) {
679             cfg.cls += ' active';
680         }
681         
682         if (this.disabled) {
683             cfg.disabled = 'disabled';
684         }
685         
686         if (this.items) {
687             Roo.log('changing to ul' );
688             cfg.tag = 'ul';
689             this.glyphicon = 'caret';
690         }
691         
692         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
693          
694         //gsRoo.log(this.parentType);
695         if (this.parentType === 'Navbar' && !this.parent().bar) {
696             Roo.log('changing to li?');
697             
698             cfg.tag = 'li';
699             
700             cfg.cls = '';
701             cfg.cn =  [{
702                 tag : 'a',
703                 cls : 'roo-button',
704                 html : this.html,
705                 href : this.href || '#'
706             }];
707             if (this.menu) {
708                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
709                 cfg.cls += ' dropdown';
710             }   
711             
712             delete cfg.html;
713             
714         }
715         
716        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
717         
718         if (this.glyphicon) {
719             cfg.html = ' ' + cfg.html;
720             
721             cfg.cn = [
722                 {
723                     tag: 'span',
724                     cls: 'glyphicon glyphicon-' + this.glyphicon
725                 }
726             ];
727         }
728         
729         if (this.badge) {
730             cfg.html += ' ';
731             
732             cfg.tag = 'a';
733             
734 //            cfg.cls='btn roo-button';
735             
736             cfg.href=this.href;
737             
738             var value = cfg.html;
739             
740             if(this.glyphicon){
741                 value = {
742                             tag: 'span',
743                             cls: 'glyphicon glyphicon-' + this.glyphicon,
744                             html: this.html
745                         };
746                 
747             }
748             
749             cfg.cn = [
750                 value,
751                 {
752                     tag: 'span',
753                     cls: 'badge',
754                     html: this.badge
755                 }
756             ];
757             
758             cfg.html='';
759         }
760         
761         if (this.menu) {
762             cfg.cls += ' dropdown';
763             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
764         }
765         
766         if (cfg.tag !== 'a' && this.href !== '') {
767             throw "Tag must be a to set href.";
768         } else if (this.href.length > 0) {
769             cfg.href = this.href;
770         }
771         
772         if(this.removeClass){
773             cfg.cls = '';
774         }
775         
776         if(this.target){
777             cfg.target = this.target;
778         }
779         
780         return cfg;
781     },
782     initEvents: function() {
783        // Roo.log('init events?');
784 //        Roo.log(this.el.dom);
785         // add the menu...
786         
787         if (typeof (this.menu) != 'undefined') {
788             this.menu.parentType = this.xtype;
789             this.menu.triggerEl = this.el;
790             this.addxtype(Roo.apply({}, this.menu));
791         }
792
793
794        if (this.el.hasClass('roo-button')) {
795             this.el.on('click', this.onClick, this);
796        } else {
797             this.el.select('.roo-button').on('click', this.onClick, this);
798        }
799        
800        if(this.removeClass){
801            this.el.on('click', this.onClick, this);
802        }
803        
804        this.el.enableDisplayMode();
805         
806     },
807     onClick : function(e)
808     {
809         if (this.disabled) {
810             return;
811         }
812         
813         
814         Roo.log('button on click ');
815         if(this.preventDefault){
816             e.preventDefault();
817         }
818         if (this.pressed === true || this.pressed === false) {
819             this.pressed = !this.pressed;
820             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
821             this.fireEvent('toggle', this, e, this.pressed);
822         }
823         
824         
825         this.fireEvent('click', this, e);
826     },
827     
828     /**
829      * Enables this button
830      */
831     enable : function()
832     {
833         this.disabled = false;
834         this.el.removeClass('disabled');
835     },
836     
837     /**
838      * Disable this button
839      */
840     disable : function()
841     {
842         this.disabled = true;
843         this.el.addClass('disabled');
844     },
845      /**
846      * sets the active state on/off, 
847      * @param {Boolean} state (optional) Force a particular state
848      */
849     setActive : function(v) {
850         
851         this.el[v ? 'addClass' : 'removeClass']('active');
852     },
853      /**
854      * toggles the current active state 
855      */
856     toggleActive : function()
857     {
858        var active = this.el.hasClass('active');
859        this.setActive(!active);
860        
861         
862     },
863     setText : function(str)
864     {
865         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
866     },
867     getText : function()
868     {
869         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870     },
871     hide: function() {
872        
873      
874         this.el.hide();   
875     },
876     show: function() {
877        
878         this.el.show();   
879     },
880     setWeight : function(str)
881     {
882           this.el.removeClass(this.weightClass);
883         this.el.addClass('btn-' + str);        
884     }
885     
886     
887 });
888
889  /*
890  * - LGPL
891  *
892  * column
893  * 
894  */
895
896 /**
897  * @class Roo.bootstrap.Column
898  * @extends Roo.bootstrap.Component
899  * Bootstrap Column class
900  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
901  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
902  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
903  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
904  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
905  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
906  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
907  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
908  *
909  * 
910  * @cfg {Boolean} hidden (true|false) hide the element
911  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
912  * @cfg {String} fa (ban|check|...) font awesome icon
913  * @cfg {Number} fasize (1|2|....) font awsome size
914
915  * @cfg {String} icon (info-sign|check|...) glyphicon name
916
917  * @cfg {String} html content of column.
918  * 
919  * @constructor
920  * Create a new Column
921  * @param {Object} config The config object
922  */
923
924 Roo.bootstrap.Column = function(config){
925     Roo.bootstrap.Column.superclass.constructor.call(this, config);
926 };
927
928 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
929     
930     xs: false,
931     sm: false,
932     md: false,
933     lg: false,
934     xsoff: false,
935     smoff: false,
936     mdoff: false,
937     lgoff: false,
938     html: '',
939     offset: 0,
940     alert: false,
941     fa: false,
942     icon : false,
943     hidden : false,
944     fasize : 1,
945     
946     getAutoCreate : function(){
947         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
948         
949         cfg = {
950             tag: 'div',
951             cls: 'column'
952         };
953         
954         var settings=this;
955         ['xs','sm','md','lg'].map(function(size){
956             //Roo.log( size + ':' + settings[size]);
957             
958             if (settings[size+'off'] !== false) {
959                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
960             }
961             
962             if (settings[size] === false) {
963                 return;
964             }
965             
966             if (!settings[size]) { // 0 = hidden
967                 cfg.cls += ' hidden-' + size;
968                 return;
969             }
970             cfg.cls += ' col-' + size + '-' + settings[size];
971             
972         });
973         
974         if (this.hidden) {
975             cfg.cls += ' hidden';
976         }
977         
978         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
979             cfg.cls +=' alert alert-' + this.alert;
980         }
981         
982         
983         if (this.html.length) {
984             cfg.html = this.html;
985         }
986         if (this.fa) {
987             var fasize = '';
988             if (this.fasize > 1) {
989                 fasize = ' fa-' + this.fasize + 'x';
990             }
991             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
992             
993             
994         }
995         if (this.icon) {
996             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
997         }
998         
999         return cfg;
1000     }
1001    
1002 });
1003
1004  
1005
1006  /*
1007  * - LGPL
1008  *
1009  * page container.
1010  * 
1011  */
1012
1013
1014 /**
1015  * @class Roo.bootstrap.Container
1016  * @extends Roo.bootstrap.Component
1017  * Bootstrap Container class
1018  * @cfg {Boolean} jumbotron is it a jumbotron element
1019  * @cfg {String} html content of element
1020  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1021  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1022  * @cfg {String} header content of header (for panel)
1023  * @cfg {String} footer content of footer (for panel)
1024  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1025  * @cfg {String} tag (header|aside|section) type of HTML tag.
1026  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1027  * @cfg {String} fa font awesome icon
1028  * @cfg {String} icon (info-sign|check|...) glyphicon name
1029  * @cfg {Boolean} hidden (true|false) hide the element
1030  * @cfg {Boolean} expandable (true|false) default false
1031  * @cfg {Boolean} expanded (true|false) default true
1032  * @cfg {String} rheader contet on the right of header
1033  * @cfg {Boolean} clickable (true|false) default false
1034
1035  *     
1036  * @constructor
1037  * Create a new Container
1038  * @param {Object} config The config object
1039  */
1040
1041 Roo.bootstrap.Container = function(config){
1042     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1043     
1044     this.addEvents({
1045         // raw events
1046          /**
1047          * @event expand
1048          * After the panel has been expand
1049          * 
1050          * @param {Roo.bootstrap.Container} this
1051          */
1052         "expand" : true,
1053         /**
1054          * @event collapse
1055          * After the panel has been collapsed
1056          * 
1057          * @param {Roo.bootstrap.Container} this
1058          */
1059         "collapse" : true,
1060         /**
1061          * @event click
1062          * When a element is chick
1063          * @param {Roo.bootstrap.Container} this
1064          * @param {Roo.EventObject} e
1065          */
1066         "click" : true
1067     });
1068 };
1069
1070 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1071     
1072     jumbotron : false,
1073     well: '',
1074     panel : '',
1075     header: '',
1076     footer : '',
1077     sticky: '',
1078     tag : false,
1079     alert : false,
1080     fa: false,
1081     icon : false,
1082     expandable : false,
1083     rheader : '',
1084     expanded : true,
1085     clickable: false,
1086   
1087      
1088     getChildContainer : function() {
1089         
1090         if(!this.el){
1091             return false;
1092         }
1093         
1094         if (this.panel.length) {
1095             return this.el.select('.panel-body',true).first();
1096         }
1097         
1098         return this.el;
1099     },
1100     
1101     
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag : this.tag || 'div',
1106             html : '',
1107             cls : ''
1108         };
1109         if (this.jumbotron) {
1110             cfg.cls = 'jumbotron';
1111         }
1112         
1113         
1114         
1115         // - this is applied by the parent..
1116         //if (this.cls) {
1117         //    cfg.cls = this.cls + '';
1118         //}
1119         
1120         if (this.sticky.length) {
1121             
1122             var bd = Roo.get(document.body);
1123             if (!bd.hasClass('bootstrap-sticky')) {
1124                 bd.addClass('bootstrap-sticky');
1125                 Roo.select('html',true).setStyle('height', '100%');
1126             }
1127              
1128             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1129         }
1130         
1131         
1132         if (this.well.length) {
1133             switch (this.well) {
1134                 case 'lg':
1135                 case 'sm':
1136                     cfg.cls +=' well well-' +this.well;
1137                     break;
1138                 default:
1139                     cfg.cls +=' well';
1140                     break;
1141             }
1142         }
1143         
1144         if (this.hidden) {
1145             cfg.cls += ' hidden';
1146         }
1147         
1148         
1149         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1150             cfg.cls +=' alert alert-' + this.alert;
1151         }
1152         
1153         var body = cfg;
1154         
1155         if (this.panel.length) {
1156             cfg.cls += ' panel panel-' + this.panel;
1157             cfg.cn = [];
1158             if (this.header.length) {
1159                 
1160                 var h = [];
1161                 
1162                 if(this.expandable){
1163                     
1164                     cfg.cls = cfg.cls + ' expandable';
1165                     
1166                     h.push({
1167                         tag: 'i',
1168                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1169                     });
1170                     
1171                 }
1172                 
1173                 h.push(
1174                     {
1175                         tag: 'span',
1176                         cls : 'panel-title',
1177                         html : (this.expandable ? '&nbsp;' : '') + this.header
1178                     },
1179                     {
1180                         tag: 'span',
1181                         cls: 'panel-header-right',
1182                         html: this.rheader
1183                     }
1184                 );
1185                 
1186                 cfg.cn.push({
1187                     cls : 'panel-heading',
1188                     style : this.expandable ? 'cursor: pointer' : '',
1189                     cn : h
1190                 });
1191                 
1192             }
1193             
1194             body = false;
1195             cfg.cn.push({
1196                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1197                 html : this.html
1198             });
1199             
1200             
1201             if (this.footer.length) {
1202                 cfg.cn.push({
1203                     cls : 'panel-footer',
1204                     html : this.footer
1205                     
1206                 });
1207             }
1208             
1209         }
1210         
1211         if (body) {
1212             body.html = this.html || cfg.html;
1213             // prefix with the icons..
1214             if (this.fa) {
1215                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1216             }
1217             if (this.icon) {
1218                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1219             }
1220             
1221             
1222         }
1223         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1224             cfg.cls =  'container';
1225         }
1226         
1227         return cfg;
1228     },
1229     
1230     initEvents: function() 
1231     {
1232         if(this.expandable){
1233             var headerEl = this.headerEl();
1234         
1235             if(headerEl){
1236                 headerEl.on('click', this.onToggleClick, this);
1237             }
1238         }
1239         
1240         if(this.clickable){
1241             this.el.on('click', this.onClick, this);
1242         }
1243         
1244     },
1245     
1246     onToggleClick : function()
1247     {
1248         var headerEl = this.headerEl();
1249         
1250         if(!headerEl){
1251             return;
1252         }
1253         
1254         if(this.expanded){
1255             this.collapse();
1256             return;
1257         }
1258         
1259         this.expand();
1260     },
1261     
1262     expand : function()
1263     {
1264         if(this.fireEvent('expand', this)) {
1265             
1266             this.expanded = true;
1267             
1268             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1269             
1270             this.el.select('.panel-body',true).first().removeClass('hide');
1271             
1272             var toggleEl = this.toggleEl();
1273
1274             if(!toggleEl){
1275                 return;
1276             }
1277
1278             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1279         }
1280         
1281     },
1282     
1283     collapse : function()
1284     {
1285         if(this.fireEvent('collapse', this)) {
1286             
1287             this.expanded = false;
1288             
1289             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1290             this.el.select('.panel-body',true).first().addClass('hide');
1291         
1292             var toggleEl = this.toggleEl();
1293
1294             if(!toggleEl){
1295                 return;
1296             }
1297
1298             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1299         }
1300     },
1301     
1302     toggleEl : function()
1303     {
1304         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1305             return;
1306         }
1307         
1308         return this.el.select('.panel-heading .fa',true).first();
1309     },
1310     
1311     headerEl : function()
1312     {
1313         if(!this.el || !this.panel.length || !this.header.length){
1314             return;
1315         }
1316         
1317         return this.el.select('.panel-heading',true).first()
1318     },
1319     
1320     bodyEl : function()
1321     {
1322         if(!this.el || !this.panel.length){
1323             return;
1324         }
1325         
1326         return this.el.select('.panel-body',true).first()
1327     },
1328     
1329     titleEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-title',true).first();
1336     },
1337     
1338     setTitle : function(v)
1339     {
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return;
1344         }
1345         
1346         titleEl.dom.innerHTML = v;
1347     },
1348     
1349     getTitle : function()
1350     {
1351         
1352         var titleEl = this.titleEl();
1353         
1354         if(!titleEl){
1355             return '';
1356         }
1357         
1358         return titleEl.dom.innerHTML;
1359     },
1360     
1361     setRightTitle : function(v)
1362     {
1363         var t = this.el.select('.panel-header-right',true).first();
1364         
1365         if(!t){
1366             return;
1367         }
1368         
1369         t.dom.innerHTML = v;
1370     },
1371     
1372     onClick : function(e)
1373     {
1374         e.preventDefault();
1375         
1376         this.fireEvent('click', this, e);
1377     }
1378    
1379 });
1380
1381  /*
1382  * - LGPL
1383  *
1384  * image
1385  * 
1386  */
1387
1388
1389 /**
1390  * @class Roo.bootstrap.Img
1391  * @extends Roo.bootstrap.Component
1392  * Bootstrap Img class
1393  * @cfg {Boolean} imgResponsive false | true
1394  * @cfg {String} border rounded | circle | thumbnail
1395  * @cfg {String} src image source
1396  * @cfg {String} alt image alternative text
1397  * @cfg {String} href a tag href
1398  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1399  * @cfg {String} xsUrl xs image source
1400  * @cfg {String} smUrl sm image source
1401  * @cfg {String} mdUrl md image source
1402  * @cfg {String} lgUrl lg image source
1403  * 
1404  * @constructor
1405  * Create a new Input
1406  * @param {Object} config The config object
1407  */
1408
1409 Roo.bootstrap.Img = function(config){
1410     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1411     
1412     this.addEvents({
1413         // img events
1414         /**
1415          * @event click
1416          * The img click event for the img.
1417          * @param {Roo.EventObject} e
1418          */
1419         "click" : true
1420     });
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1424     
1425     imgResponsive: true,
1426     border: '',
1427     src: 'about:blank',
1428     href: false,
1429     target: false,
1430     xsUrl: '',
1431     smUrl: '',
1432     mdUrl: '',
1433     lgUrl: '',
1434
1435     getAutoCreate : function()
1436     {   
1437         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1438             return this.createSingleImg();
1439         }
1440         
1441         var cfg = {
1442             tag: 'div',
1443             cls: 'roo-image-responsive-group',
1444             cn: []
1445         };
1446         var _this = this;
1447         
1448         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1449             
1450             if(!_this[size + 'Url']){
1451                 return;
1452             }
1453             
1454             var img = {
1455                 tag: 'img',
1456                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1457                 html: _this.html || cfg.html,
1458                 src: _this[size + 'Url']
1459             };
1460             
1461             img.cls += ' roo-image-responsive-' + size;
1462             
1463             var s = ['xs', 'sm', 'md', 'lg'];
1464             
1465             s.splice(s.indexOf(size), 1);
1466             
1467             Roo.each(s, function(ss){
1468                 img.cls += ' hidden-' + ss;
1469             });
1470             
1471             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1472                 cfg.cls += ' img-' + _this.border;
1473             }
1474             
1475             if(_this.alt){
1476                 cfg.alt = _this.alt;
1477             }
1478             
1479             if(_this.href){
1480                 var a = {
1481                     tag: 'a',
1482                     href: _this.href,
1483                     cn: [
1484                         img
1485                     ]
1486                 };
1487
1488                 if(this.target){
1489                     a.target = _this.target;
1490                 }
1491             }
1492             
1493             cfg.cn.push((_this.href) ? a : img);
1494             
1495         });
1496         
1497         return cfg;
1498     },
1499     
1500     createSingleImg : function()
1501     {
1502         var cfg = {
1503             tag: 'img',
1504             cls: (this.imgResponsive) ? 'img-responsive' : '',
1505             html : null,
1506             src : 'about:blank'  // just incase src get's set to undefined?!?
1507         };
1508         
1509         cfg.html = this.html || cfg.html;
1510         
1511         cfg.src = this.src || cfg.src;
1512         
1513         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1514             cfg.cls += ' img-' + this.border;
1515         }
1516         
1517         if(this.alt){
1518             cfg.alt = this.alt;
1519         }
1520         
1521         if(this.href){
1522             var a = {
1523                 tag: 'a',
1524                 href: this.href,
1525                 cn: [
1526                     cfg
1527                 ]
1528             };
1529             
1530             if(this.target){
1531                 a.target = this.target;
1532             }
1533             
1534         }
1535         
1536         return (this.href) ? a : cfg;
1537     },
1538     
1539     initEvents: function() 
1540     {
1541         if(!this.href){
1542             this.el.on('click', this.onClick, this);
1543         }
1544         
1545     },
1546     
1547     onClick : function(e)
1548     {
1549         Roo.log('img onclick');
1550         this.fireEvent('click', this, e);
1551     },
1552     /**
1553      * Sets the url of the image - used to update it
1554      * @param {String} url the url of the image
1555      */
1556     
1557     setSrc : function(url)
1558     {
1559         this.src =  url;
1560         
1561         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1562             this.el.dom.src =  url;
1563             return;
1564         }
1565         
1566         this.el.select('img', true).first().dom.src =  url;
1567     }
1568     
1569     
1570    
1571 });
1572
1573  /*
1574  * - LGPL
1575  *
1576  * image
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Link
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Link Class
1585  * @cfg {String} alt image alternative text
1586  * @cfg {String} href a tag href
1587  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1588  * @cfg {String} html the content of the link.
1589  * @cfg {String} anchor name for the anchor link
1590  * @cfg {String} fa - favicon
1591
1592  * @cfg {Boolean} preventDefault (true | false) default false
1593
1594  * 
1595  * @constructor
1596  * Create a new Input
1597  * @param {Object} config The config object
1598  */
1599
1600 Roo.bootstrap.Link = function(config){
1601     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1602     
1603     this.addEvents({
1604         // img events
1605         /**
1606          * @event click
1607          * The img click event for the img.
1608          * @param {Roo.EventObject} e
1609          */
1610         "click" : true
1611     });
1612 };
1613
1614 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1615     
1616     href: false,
1617     target: false,
1618     preventDefault: false,
1619     anchor : false,
1620     alt : false,
1621     fa: false,
1622
1623
1624     getAutoCreate : function()
1625     {
1626         var html = this.html || '';
1627         
1628         if (this.fa !== false) {
1629             html = '<i class="fa fa-' + this.fa + '"></i>';
1630         }
1631         var cfg = {
1632             tag: 'a'
1633         };
1634         // anchor's do not require html/href...
1635         if (this.anchor === false) {
1636             cfg.html = html;
1637             cfg.href = this.href || '#';
1638         } else {
1639             cfg.name = this.anchor;
1640             if (this.html !== false || this.fa !== false) {
1641                 cfg.html = html;
1642             }
1643             if (this.href !== false) {
1644                 cfg.href = this.href;
1645             }
1646         }
1647         
1648         if(this.alt !== false){
1649             cfg.alt = this.alt;
1650         }
1651         
1652         
1653         if(this.target !== false) {
1654             cfg.target = this.target;
1655         }
1656         
1657         return cfg;
1658     },
1659     
1660     initEvents: function() {
1661         
1662         if(!this.href || this.preventDefault){
1663             this.el.on('click', this.onClick, this);
1664         }
1665     },
1666     
1667     onClick : function(e)
1668     {
1669         if(this.preventDefault){
1670             e.preventDefault();
1671         }
1672         //Roo.log('img onclick');
1673         this.fireEvent('click', this, e);
1674     }
1675    
1676 });
1677
1678  /*
1679  * - LGPL
1680  *
1681  * header
1682  * 
1683  */
1684
1685 /**
1686  * @class Roo.bootstrap.Header
1687  * @extends Roo.bootstrap.Component
1688  * Bootstrap Header class
1689  * @cfg {String} html content of header
1690  * @cfg {Number} level (1|2|3|4|5|6) default 1
1691  * 
1692  * @constructor
1693  * Create a new Header
1694  * @param {Object} config The config object
1695  */
1696
1697
1698 Roo.bootstrap.Header  = function(config){
1699     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1700 };
1701
1702 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1703     
1704     //href : false,
1705     html : false,
1706     level : 1,
1707     
1708     
1709     
1710     getAutoCreate : function(){
1711         
1712         
1713         
1714         var cfg = {
1715             tag: 'h' + (1 *this.level),
1716             html: this.html || ''
1717         } ;
1718         
1719         return cfg;
1720     }
1721    
1722 });
1723
1724  
1725
1726  /*
1727  * Based on:
1728  * Ext JS Library 1.1.1
1729  * Copyright(c) 2006-2007, Ext JS, LLC.
1730  *
1731  * Originally Released Under LGPL - original licence link has changed is not relivant.
1732  *
1733  * Fork - LGPL
1734  * <script type="text/javascript">
1735  */
1736  
1737 /**
1738  * @class Roo.bootstrap.MenuMgr
1739  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1740  * @singleton
1741  */
1742 Roo.bootstrap.MenuMgr = function(){
1743    var menus, active, groups = {}, attached = false, lastShow = new Date();
1744
1745    // private - called when first menu is created
1746    function init(){
1747        menus = {};
1748        active = new Roo.util.MixedCollection();
1749        Roo.get(document).addKeyListener(27, function(){
1750            if(active.length > 0){
1751                hideAll();
1752            }
1753        });
1754    }
1755
1756    // private
1757    function hideAll(){
1758        if(active && active.length > 0){
1759            var c = active.clone();
1760            c.each(function(m){
1761                m.hide();
1762            });
1763        }
1764    }
1765
1766    // private
1767    function onHide(m){
1768        active.remove(m);
1769        if(active.length < 1){
1770            Roo.get(document).un("mouseup", onMouseDown);
1771             
1772            attached = false;
1773        }
1774    }
1775
1776    // private
1777    function onShow(m){
1778        var last = active.last();
1779        lastShow = new Date();
1780        active.add(m);
1781        if(!attached){
1782           Roo.get(document).on("mouseup", onMouseDown);
1783            
1784            attached = true;
1785        }
1786        if(m.parentMenu){
1787           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1788           m.parentMenu.activeChild = m;
1789        }else if(last && last.isVisible()){
1790           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1791        }
1792    }
1793
1794    // private
1795    function onBeforeHide(m){
1796        if(m.activeChild){
1797            m.activeChild.hide();
1798        }
1799        if(m.autoHideTimer){
1800            clearTimeout(m.autoHideTimer);
1801            delete m.autoHideTimer;
1802        }
1803    }
1804
1805    // private
1806    function onBeforeShow(m){
1807        var pm = m.parentMenu;
1808        if(!pm && !m.allowOtherMenus){
1809            hideAll();
1810        }else if(pm && pm.activeChild && active != m){
1811            pm.activeChild.hide();
1812        }
1813    }
1814
1815    // private this should really trigger on mouseup..
1816    function onMouseDown(e){
1817         Roo.log("on Mouse Up");
1818         
1819         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1820             Roo.log("MenuManager hideAll");
1821             hideAll();
1822             e.stopEvent();
1823         }
1824         
1825         
1826    }
1827
1828    // private
1829    function onBeforeCheck(mi, state){
1830        if(state){
1831            var g = groups[mi.group];
1832            for(var i = 0, l = g.length; i < l; i++){
1833                if(g[i] != mi){
1834                    g[i].setChecked(false);
1835                }
1836            }
1837        }
1838    }
1839
1840    return {
1841
1842        /**
1843         * Hides all menus that are currently visible
1844         */
1845        hideAll : function(){
1846             hideAll();  
1847        },
1848
1849        // private
1850        register : function(menu){
1851            if(!menus){
1852                init();
1853            }
1854            menus[menu.id] = menu;
1855            menu.on("beforehide", onBeforeHide);
1856            menu.on("hide", onHide);
1857            menu.on("beforeshow", onBeforeShow);
1858            menu.on("show", onShow);
1859            var g = menu.group;
1860            if(g && menu.events["checkchange"]){
1861                if(!groups[g]){
1862                    groups[g] = [];
1863                }
1864                groups[g].push(menu);
1865                menu.on("checkchange", onCheck);
1866            }
1867        },
1868
1869         /**
1870          * Returns a {@link Roo.menu.Menu} object
1871          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1872          * be used to generate and return a new Menu instance.
1873          */
1874        get : function(menu){
1875            if(typeof menu == "string"){ // menu id
1876                return menus[menu];
1877            }else if(menu.events){  // menu instance
1878                return menu;
1879            }
1880            /*else if(typeof menu.length == 'number'){ // array of menu items?
1881                return new Roo.bootstrap.Menu({items:menu});
1882            }else{ // otherwise, must be a config
1883                return new Roo.bootstrap.Menu(menu);
1884            }
1885            */
1886            return false;
1887        },
1888
1889        // private
1890        unregister : function(menu){
1891            delete menus[menu.id];
1892            menu.un("beforehide", onBeforeHide);
1893            menu.un("hide", onHide);
1894            menu.un("beforeshow", onBeforeShow);
1895            menu.un("show", onShow);
1896            var g = menu.group;
1897            if(g && menu.events["checkchange"]){
1898                groups[g].remove(menu);
1899                menu.un("checkchange", onCheck);
1900            }
1901        },
1902
1903        // private
1904        registerCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                if(!groups[g]){
1908                    groups[g] = [];
1909                }
1910                groups[g].push(menuItem);
1911                menuItem.on("beforecheckchange", onBeforeCheck);
1912            }
1913        },
1914
1915        // private
1916        unregisterCheckable : function(menuItem){
1917            var g = menuItem.group;
1918            if(g){
1919                groups[g].remove(menuItem);
1920                menuItem.un("beforecheckchange", onBeforeCheck);
1921            }
1922        }
1923    };
1924 }();/*
1925  * - LGPL
1926  *
1927  * menu
1928  * 
1929  */
1930
1931 /**
1932  * @class Roo.bootstrap.Menu
1933  * @extends Roo.bootstrap.Component
1934  * Bootstrap Menu class - container for MenuItems
1935  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1936  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1937  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1938  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1939  * 
1940  * @constructor
1941  * Create a new Menu
1942  * @param {Object} config The config object
1943  */
1944
1945
1946 Roo.bootstrap.Menu = function(config){
1947     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1948     if (this.registerMenu && this.type != 'treeview')  {
1949         Roo.bootstrap.MenuMgr.register(this);
1950     }
1951     this.addEvents({
1952         /**
1953          * @event beforeshow
1954          * Fires before this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         beforeshow : true,
1958         /**
1959          * @event beforehide
1960          * Fires before this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         beforehide : true,
1964         /**
1965          * @event show
1966          * Fires after this menu is displayed
1967          * @param {Roo.menu.Menu} this
1968          */
1969         show : true,
1970         /**
1971          * @event hide
1972          * Fires after this menu is hidden
1973          * @param {Roo.menu.Menu} this
1974          */
1975         hide : true,
1976         /**
1977          * @event click
1978          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1979          * @param {Roo.menu.Menu} this
1980          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1981          * @param {Roo.EventObject} e
1982          */
1983         click : true,
1984         /**
1985          * @event mouseover
1986          * Fires when the mouse is hovering over this menu
1987          * @param {Roo.menu.Menu} this
1988          * @param {Roo.EventObject} e
1989          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1990          */
1991         mouseover : true,
1992         /**
1993          * @event mouseout
1994          * Fires when the mouse exits this menu
1995          * @param {Roo.menu.Menu} this
1996          * @param {Roo.EventObject} e
1997          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1998          */
1999         mouseout : true,
2000         /**
2001          * @event itemclick
2002          * Fires when a menu item contained in this menu is clicked
2003          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2004          * @param {Roo.EventObject} e
2005          */
2006         itemclick: true
2007     });
2008     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2009 };
2010
2011 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2012     
2013    /// html : false,
2014     //align : '',
2015     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2016     type: false,
2017     /**
2018      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2019      */
2020     registerMenu : true,
2021     
2022     menuItems :false, // stores the menu items..
2023     
2024     hidden:true,
2025         
2026     parentMenu : false,
2027     
2028     stopEvent : true,
2029     
2030     isLink : false,
2031     
2032     getChildContainer : function() {
2033         return this.el;  
2034     },
2035     
2036     getAutoCreate : function(){
2037          
2038         //if (['right'].indexOf(this.align)!==-1) {
2039         //    cfg.cn[1].cls += ' pull-right'
2040         //}
2041         
2042         
2043         var cfg = {
2044             tag : 'ul',
2045             cls : 'dropdown-menu' ,
2046             style : 'z-index:1000'
2047             
2048         };
2049         
2050         if (this.type === 'submenu') {
2051             cfg.cls = 'submenu active';
2052         }
2053         if (this.type === 'treeview') {
2054             cfg.cls = 'treeview-menu';
2055         }
2056         
2057         return cfg;
2058     },
2059     initEvents : function() {
2060         
2061        // Roo.log("ADD event");
2062        // Roo.log(this.triggerEl.dom);
2063         
2064         this.triggerEl.on('click', this.onTriggerClick, this);
2065         
2066         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2067         
2068         this.triggerEl.addClass('dropdown-toggle');
2069         
2070         if (Roo.isTouch) {
2071             this.el.on('touchstart'  , this.onTouch, this);
2072         }
2073         this.el.on('click' , this.onClick, this);
2074
2075         this.el.on("mouseover", this.onMouseOver, this);
2076         this.el.on("mouseout", this.onMouseOut, this);
2077         
2078     },
2079     
2080     findTargetItem : function(e)
2081     {
2082         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2083         if(!t){
2084             return false;
2085         }
2086         //Roo.log(t);         Roo.log(t.id);
2087         if(t && t.id){
2088             //Roo.log(this.menuitems);
2089             return this.menuitems.get(t.id);
2090             
2091             //return this.items.get(t.menuItemId);
2092         }
2093         
2094         return false;
2095     },
2096     
2097     onTouch : function(e) 
2098     {
2099         Roo.log("menu.onTouch");
2100         //e.stopEvent(); this make the user popdown broken
2101         this.onClick(e);
2102     },
2103     
2104     onClick : function(e)
2105     {
2106         Roo.log("menu.onClick");
2107         
2108         var t = this.findTargetItem(e);
2109         if(!t || t.isContainer){
2110             return;
2111         }
2112         Roo.log(e);
2113         /*
2114         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2115             if(t == this.activeItem && t.shouldDeactivate(e)){
2116                 this.activeItem.deactivate();
2117                 delete this.activeItem;
2118                 return;
2119             }
2120             if(t.canActivate){
2121                 this.setActiveItem(t, true);
2122             }
2123             return;
2124             
2125             
2126         }
2127         */
2128        
2129         Roo.log('pass click event');
2130         
2131         t.onClick(e);
2132         
2133         this.fireEvent("click", this, t, e);
2134         
2135         var _this = this;
2136         
2137         if(!t.href.length || t.href == '#'){
2138             (function() { _this.hide(); }).defer(100);
2139         }
2140         
2141     },
2142     
2143     onMouseOver : function(e){
2144         var t  = this.findTargetItem(e);
2145         //Roo.log(t);
2146         //if(t){
2147         //    if(t.canActivate && !t.disabled){
2148         //        this.setActiveItem(t, true);
2149         //    }
2150         //}
2151         
2152         this.fireEvent("mouseover", this, e, t);
2153     },
2154     isVisible : function(){
2155         return !this.hidden;
2156     },
2157      onMouseOut : function(e){
2158         var t  = this.findTargetItem(e);
2159         
2160         //if(t ){
2161         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2162         //        this.activeItem.deactivate();
2163         //        delete this.activeItem;
2164         //    }
2165         //}
2166         this.fireEvent("mouseout", this, e, t);
2167     },
2168     
2169     
2170     /**
2171      * Displays this menu relative to another element
2172      * @param {String/HTMLElement/Roo.Element} element The element to align to
2173      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2174      * the element (defaults to this.defaultAlign)
2175      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2176      */
2177     show : function(el, pos, parentMenu){
2178         this.parentMenu = parentMenu;
2179         if(!this.el){
2180             this.render();
2181         }
2182         this.fireEvent("beforeshow", this);
2183         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2184     },
2185      /**
2186      * Displays this menu at a specific xy position
2187      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2188      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2189      */
2190     showAt : function(xy, parentMenu, /* private: */_e){
2191         this.parentMenu = parentMenu;
2192         if(!this.el){
2193             this.render();
2194         }
2195         if(_e !== false){
2196             this.fireEvent("beforeshow", this);
2197             //xy = this.el.adjustForConstraints(xy);
2198         }
2199         
2200         //this.el.show();
2201         this.hideMenuItems();
2202         this.hidden = false;
2203         this.triggerEl.addClass('open');
2204         
2205         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2206             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2207         }
2208         
2209         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2210             this.el.setXY(xy);
2211         }
2212         
2213         this.focus();
2214         this.fireEvent("show", this);
2215     },
2216     
2217     focus : function(){
2218         return;
2219         if(!this.hidden){
2220             this.doFocus.defer(50, this);
2221         }
2222     },
2223
2224     doFocus : function(){
2225         if(!this.hidden){
2226             this.focusEl.focus();
2227         }
2228     },
2229
2230     /**
2231      * Hides this menu and optionally all parent menus
2232      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2233      */
2234     hide : function(deep)
2235     {
2236         
2237         this.hideMenuItems();
2238         if(this.el && this.isVisible()){
2239             this.fireEvent("beforehide", this);
2240             if(this.activeItem){
2241                 this.activeItem.deactivate();
2242                 this.activeItem = null;
2243             }
2244             this.triggerEl.removeClass('open');;
2245             this.hidden = true;
2246             this.fireEvent("hide", this);
2247         }
2248         if(deep === true && this.parentMenu){
2249             this.parentMenu.hide(true);
2250         }
2251     },
2252     
2253     onTriggerClick : function(e)
2254     {
2255         Roo.log('trigger click');
2256         
2257         var target = e.getTarget();
2258         
2259         Roo.log(target.nodeName.toLowerCase());
2260         
2261         if(target.nodeName.toLowerCase() === 'i'){
2262             e.preventDefault();
2263         }
2264         
2265     },
2266     
2267     onTriggerPress  : function(e)
2268     {
2269         Roo.log('trigger press');
2270         //Roo.log(e.getTarget());
2271        // Roo.log(this.triggerEl.dom);
2272        
2273         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2274         var pel = Roo.get(e.getTarget());
2275         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2276             Roo.log('is treeview or dropdown?');
2277             return;
2278         }
2279         
2280         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2281             return;
2282         }
2283         
2284         if (this.isVisible()) {
2285             Roo.log('hide');
2286             this.hide();
2287         } else {
2288             Roo.log('show');
2289             this.show(this.triggerEl, false, false);
2290         }
2291         
2292         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2293             e.stopEvent();
2294         }
2295         
2296     },
2297        
2298     
2299     hideMenuItems : function()
2300     {
2301         Roo.log("hide Menu Items");
2302         if (!this.el) { 
2303             return;
2304         }
2305         //$(backdrop).remove()
2306         this.el.select('.open',true).each(function(aa) {
2307             
2308             aa.removeClass('open');
2309           //var parent = getParent($(this))
2310           //var relatedTarget = { relatedTarget: this }
2311           
2312            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2313           //if (e.isDefaultPrevented()) return
2314            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2315         });
2316     },
2317     addxtypeChild : function (tree, cntr) {
2318         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2319           
2320         this.menuitems.add(comp);
2321         return comp;
2322
2323     },
2324     getEl : function()
2325     {
2326         Roo.log(this.el);
2327         return this.el;
2328     }
2329 });
2330
2331  
2332  /*
2333  * - LGPL
2334  *
2335  * menu item
2336  * 
2337  */
2338
2339
2340 /**
2341  * @class Roo.bootstrap.MenuItem
2342  * @extends Roo.bootstrap.Component
2343  * Bootstrap MenuItem class
2344  * @cfg {String} html the menu label
2345  * @cfg {String} href the link
2346  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2347  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2348  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2349  * @cfg {String} fa favicon to show on left of menu item.
2350  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2351  * 
2352  * 
2353  * @constructor
2354  * Create a new MenuItem
2355  * @param {Object} config The config object
2356  */
2357
2358
2359 Roo.bootstrap.MenuItem = function(config){
2360     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2361     this.addEvents({
2362         // raw events
2363         /**
2364          * @event click
2365          * The raw click event for the entire grid.
2366          * @param {Roo.bootstrap.MenuItem} this
2367          * @param {Roo.EventObject} e
2368          */
2369         "click" : true
2370     });
2371 };
2372
2373 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2374     
2375     href : false,
2376     html : false,
2377     preventDefault: false,
2378     isContainer : false,
2379     active : false,
2380     fa: false,
2381     
2382     getAutoCreate : function(){
2383         
2384         if(this.isContainer){
2385             return {
2386                 tag: 'li',
2387                 cls: 'dropdown-menu-item'
2388             };
2389         }
2390         var ctag = {
2391             tag: 'span',
2392             html: 'Link'
2393         };
2394         
2395         var anc = {
2396             tag : 'a',
2397             href : '#',
2398             cn : [  ]
2399         };
2400         
2401         if (this.fa !== false) {
2402             anc.cn.push({
2403                 tag : 'i',
2404                 cls : 'fa fa-' + this.fa
2405             });
2406         }
2407         
2408         anc.cn.push(ctag);
2409         
2410         
2411         var cfg= {
2412             tag: 'li',
2413             cls: 'dropdown-menu-item',
2414             cn: [ anc ]
2415         };
2416         if (this.parent().type == 'treeview') {
2417             cfg.cls = 'treeview-menu';
2418         }
2419         if (this.active) {
2420             cfg.cls += ' active';
2421         }
2422         
2423         
2424         
2425         anc.href = this.href || cfg.cn[0].href ;
2426         ctag.html = this.html || cfg.cn[0].html ;
2427         return cfg;
2428     },
2429     
2430     initEvents: function()
2431     {
2432         if (this.parent().type == 'treeview') {
2433             this.el.select('a').on('click', this.onClick, this);
2434         }
2435         
2436         if (this.menu) {
2437             this.menu.parentType = this.xtype;
2438             this.menu.triggerEl = this.el;
2439             this.menu = this.addxtype(Roo.apply({}, this.menu));
2440         }
2441         
2442     },
2443     onClick : function(e)
2444     {
2445         Roo.log('item on click ');
2446         
2447         if(this.preventDefault){
2448             e.preventDefault();
2449         }
2450         //this.parent().hideMenuItems();
2451         
2452         this.fireEvent('click', this, e);
2453     },
2454     getEl : function()
2455     {
2456         return this.el;
2457     } 
2458 });
2459
2460  
2461
2462  /*
2463  * - LGPL
2464  *
2465  * menu separator
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuSeparator
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuSeparator class
2474  * 
2475  * @constructor
2476  * Create a new MenuItem
2477  * @param {Object} config The config object
2478  */
2479
2480
2481 Roo.bootstrap.MenuSeparator = function(config){
2482     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2483 };
2484
2485 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2486     
2487     getAutoCreate : function(){
2488         var cfg = {
2489             cls: 'divider',
2490             tag : 'li'
2491         };
2492         
2493         return cfg;
2494     }
2495    
2496 });
2497
2498  
2499
2500  
2501 /*
2502 * Licence: LGPL
2503 */
2504
2505 /**
2506  * @class Roo.bootstrap.Modal
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap Modal class
2509  * @cfg {String} title Title of dialog
2510  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2511  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2512  * @cfg {Boolean} specificTitle default false
2513  * @cfg {Array} buttons Array of buttons or standard button set..
2514  * @cfg {String} buttonPosition (left|right|center) default right
2515  * @cfg {Boolean} animate default true
2516  * @cfg {Boolean} allow_close default true
2517  * @cfg {Boolean} fitwindow default false
2518  * @cfg {String} size (sm|lg) default empty
2519  *
2520  *
2521  * @constructor
2522  * Create a new Modal Dialog
2523  * @param {Object} config The config object
2524  */
2525
2526 Roo.bootstrap.Modal = function(config){
2527     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2528     this.addEvents({
2529         // raw events
2530         /**
2531          * @event btnclick
2532          * The raw btnclick event for the button
2533          * @param {Roo.EventObject} e
2534          */
2535         "btnclick" : true,
2536         /**
2537          * @event resize
2538          * Fire when dialog resize
2539          * @param {Roo.bootstrap.Modal} this
2540          * @param {Roo.EventObject} e
2541          */
2542         "resize" : true
2543     });
2544     this.buttons = this.buttons || [];
2545
2546     if (this.tmpl) {
2547         this.tmpl = Roo.factory(this.tmpl);
2548     }
2549
2550 };
2551
2552 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2553
2554     title : 'test dialog',
2555
2556     buttons : false,
2557
2558     // set on load...
2559
2560     html: false,
2561
2562     tmp: false,
2563
2564     specificTitle: false,
2565
2566     buttonPosition: 'right',
2567
2568     allow_close : true,
2569
2570     animate : true,
2571
2572     fitwindow: false,
2573
2574
2575      // private
2576     dialogEl: false,
2577     bodyEl:  false,
2578     footerEl:  false,
2579     titleEl:  false,
2580     closeEl:  false,
2581
2582     size: '',
2583
2584
2585     onRender : function(ct, position)
2586     {
2587         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2588
2589         if(!this.el){
2590             var cfg = Roo.apply({},  this.getAutoCreate());
2591             cfg.id = Roo.id();
2592             //if(!cfg.name){
2593             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2594             //}
2595             //if (!cfg.name.length) {
2596             //    delete cfg.name;
2597            // }
2598             if (this.cls) {
2599                 cfg.cls += ' ' + this.cls;
2600             }
2601             if (this.style) {
2602                 cfg.style = this.style;
2603             }
2604             this.el = Roo.get(document.body).createChild(cfg, position);
2605         }
2606         //var type = this.el.dom.type;
2607
2608
2609         if(this.tabIndex !== undefined){
2610             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2611         }
2612
2613         this.dialogEl = this.el.select('.modal-dialog',true).first();
2614         this.bodyEl = this.el.select('.modal-body',true).first();
2615         this.closeEl = this.el.select('.modal-header .close', true).first();
2616         this.headerEl = this.el.select('.modal-header',true).first();
2617         this.titleEl = this.el.select('.modal-title',true).first();
2618         this.footerEl = this.el.select('.modal-footer',true).first();
2619
2620         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2621         this.maskEl.enableDisplayMode("block");
2622         this.maskEl.hide();
2623         //this.el.addClass("x-dlg-modal");
2624
2625         if (this.buttons.length) {
2626             Roo.each(this.buttons, function(bb) {
2627                 var b = Roo.apply({}, bb);
2628                 b.xns = b.xns || Roo.bootstrap;
2629                 b.xtype = b.xtype || 'Button';
2630                 if (typeof(b.listeners) == 'undefined') {
2631                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2632                 }
2633
2634                 var btn = Roo.factory(b);
2635
2636                 btn.render(this.el.select('.modal-footer div').first());
2637
2638             },this);
2639         }
2640         // render the children.
2641         var nitems = [];
2642
2643         if(typeof(this.items) != 'undefined'){
2644             var items = this.items;
2645             delete this.items;
2646
2647             for(var i =0;i < items.length;i++) {
2648                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2649             }
2650         }
2651
2652         this.items = nitems;
2653
2654         // where are these used - they used to be body/close/footer
2655
2656
2657         this.initEvents();
2658         //this.el.addClass([this.fieldClass, this.cls]);
2659
2660     },
2661
2662     getAutoCreate : function(){
2663
2664
2665         var bdy = {
2666                 cls : 'modal-body',
2667                 html : this.html || ''
2668         };
2669
2670         var title = {
2671             tag: 'h4',
2672             cls : 'modal-title',
2673             html : this.title
2674         };
2675
2676         if(this.specificTitle){
2677             title = this.title;
2678
2679         };
2680
2681         var header = [];
2682         if (this.allow_close) {
2683             header.push({
2684                 tag: 'button',
2685                 cls : 'close',
2686                 html : '&times'
2687             });
2688         }
2689
2690         header.push(title);
2691
2692         var size = '';
2693
2694         if(this.size.length){
2695             size = 'modal-' + this.size;
2696         }
2697
2698         var modal = {
2699             cls: "modal",
2700             style : 'display: none',
2701             cn : [
2702                 {
2703                     cls: "modal-dialog " + size,
2704                     cn : [
2705                         {
2706                             cls : "modal-content",
2707                             cn : [
2708                                 {
2709                                     cls : 'modal-header',
2710                                     cn : header
2711                                 },
2712                                 bdy,
2713                                 {
2714                                     cls : 'modal-footer',
2715                                     cn : [
2716                                         {
2717                                             tag: 'div',
2718                                             cls: 'btn-' + this.buttonPosition
2719                                         }
2720                                     ]
2721
2722                                 }
2723
2724
2725                             ]
2726
2727                         }
2728                     ]
2729
2730                 }
2731             ]
2732         };
2733
2734         if(this.animate){
2735             modal.cls += ' fade';
2736         }
2737
2738         return modal;
2739
2740     },
2741     getChildContainer : function() {
2742
2743          return this.bodyEl;
2744
2745     },
2746     getButtonContainer : function() {
2747          return this.el.select('.modal-footer div',true).first();
2748
2749     },
2750     initEvents : function()
2751     {
2752         if (this.allow_close) {
2753             this.closeEl.on('click', this.hide, this);
2754         }
2755         Roo.EventManager.onWindowResize(this.resize, this, true);
2756
2757
2758     },
2759
2760     resize : function()
2761     {
2762         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2763         if (this.fitwindow) {
2764             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2765             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2766             this.setSize(w,h);
2767         }
2768     },
2769
2770     setSize : function(w,h)
2771     {
2772         if (!w && !h) {
2773             return;
2774         }
2775         this.resizeTo(w,h);
2776     },
2777
2778     show : function() {
2779
2780         if (!this.rendered) {
2781             this.render();
2782         }
2783
2784         this.el.setStyle('display', 'block');
2785
2786         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2787             var _this = this;
2788             (function(){
2789                 this.el.addClass('in');
2790             }).defer(50, this);
2791         }else{
2792             this.el.addClass('in');
2793
2794         }
2795
2796         // not sure how we can show data in here..
2797         //if (this.tmpl) {
2798         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2799         //}
2800
2801         Roo.get(document.body).addClass("x-body-masked");
2802         
2803         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2804         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2805         this.maskEl.show();
2806         
2807         this.resize();
2808         
2809         this.fireEvent('show', this);
2810
2811         // set zindex here - otherwise it appears to be ignored...
2812         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2813
2814         (function () {
2815             this.items.forEach( function(e) {
2816                 e.layout ? e.layout() : false;
2817
2818             });
2819         }).defer(100,this);
2820
2821     },
2822     hide : function()
2823     {
2824         if(this.fireEvent("beforehide", this) !== false){
2825             this.maskEl.hide();
2826             Roo.get(document.body).removeClass("x-body-masked");
2827             this.el.removeClass('in');
2828             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2829
2830             if(this.animate){ // why
2831                 var _this = this;
2832                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2833             }else{
2834                 this.el.setStyle('display', 'none');
2835             }
2836             this.fireEvent('hide', this);
2837         }
2838     },
2839
2840     addButton : function(str, cb)
2841     {
2842
2843
2844         var b = Roo.apply({}, { html : str } );
2845         b.xns = b.xns || Roo.bootstrap;
2846         b.xtype = b.xtype || 'Button';
2847         if (typeof(b.listeners) == 'undefined') {
2848             b.listeners = { click : cb.createDelegate(this)  };
2849         }
2850
2851         var btn = Roo.factory(b);
2852
2853         btn.render(this.el.select('.modal-footer div').first());
2854
2855         return btn;
2856
2857     },
2858
2859     setDefaultButton : function(btn)
2860     {
2861         //this.el.select('.modal-footer').()
2862     },
2863     diff : false,
2864
2865     resizeTo: function(w,h)
2866     {
2867         // skip.. ?? why??
2868
2869         this.dialogEl.setWidth(w);
2870         if (this.diff === false) {
2871             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2872         }
2873
2874         this.bodyEl.setHeight(h-this.diff);
2875
2876         this.fireEvent('resize', this);
2877
2878     },
2879     setContentSize  : function(w, h)
2880     {
2881
2882     },
2883     onButtonClick: function(btn,e)
2884     {
2885         //Roo.log([a,b,c]);
2886         this.fireEvent('btnclick', btn.name, e);
2887     },
2888      /**
2889      * Set the title of the Dialog
2890      * @param {String} str new Title
2891      */
2892     setTitle: function(str) {
2893         this.titleEl.dom.innerHTML = str;
2894     },
2895     /**
2896      * Set the body of the Dialog
2897      * @param {String} str new Title
2898      */
2899     setBody: function(str) {
2900         this.bodyEl.dom.innerHTML = str;
2901     },
2902     /**
2903      * Set the body of the Dialog using the template
2904      * @param {Obj} data - apply this data to the template and replace the body contents.
2905      */
2906     applyBody: function(obj)
2907     {
2908         if (!this.tmpl) {
2909             Roo.log("Error - using apply Body without a template");
2910             //code
2911         }
2912         this.tmpl.overwrite(this.bodyEl, obj);
2913     }
2914
2915 });
2916
2917
2918 Roo.apply(Roo.bootstrap.Modal,  {
2919     /**
2920          * Button config that displays a single OK button
2921          * @type Object
2922          */
2923         OK :  [{
2924             name : 'ok',
2925             weight : 'primary',
2926             html : 'OK'
2927         }],
2928         /**
2929          * Button config that displays Yes and No buttons
2930          * @type Object
2931          */
2932         YESNO : [
2933             {
2934                 name  : 'no',
2935                 html : 'No'
2936             },
2937             {
2938                 name  :'yes',
2939                 weight : 'primary',
2940                 html : 'Yes'
2941             }
2942         ],
2943
2944         /**
2945          * Button config that displays OK and Cancel buttons
2946          * @type Object
2947          */
2948         OKCANCEL : [
2949             {
2950                name : 'cancel',
2951                 html : 'Cancel'
2952             },
2953             {
2954                 name : 'ok',
2955                 weight : 'primary',
2956                 html : 'OK'
2957             }
2958         ],
2959         /**
2960          * Button config that displays Yes, No and Cancel buttons
2961          * @type Object
2962          */
2963         YESNOCANCEL : [
2964             {
2965                 name : 'yes',
2966                 weight : 'primary',
2967                 html : 'Yes'
2968             },
2969             {
2970                 name : 'no',
2971                 html : 'No'
2972             },
2973             {
2974                 name : 'cancel',
2975                 html : 'Cancel'
2976             }
2977         ],
2978         
2979         zIndex : 10001
2980 });
2981 /*
2982  * - LGPL
2983  *
2984  * messagebox - can be used as a replace
2985  * 
2986  */
2987 /**
2988  * @class Roo.MessageBox
2989  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2990  * Example usage:
2991  *<pre><code>
2992 // Basic alert:
2993 Roo.Msg.alert('Status', 'Changes saved successfully.');
2994
2995 // Prompt for user data:
2996 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2997     if (btn == 'ok'){
2998         // process text value...
2999     }
3000 });
3001
3002 // Show a dialog using config options:
3003 Roo.Msg.show({
3004    title:'Save Changes?',
3005    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3006    buttons: Roo.Msg.YESNOCANCEL,
3007    fn: processResult,
3008    animEl: 'elId'
3009 });
3010 </code></pre>
3011  * @singleton
3012  */
3013 Roo.bootstrap.MessageBox = function(){
3014     var dlg, opt, mask, waitTimer;
3015     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3016     var buttons, activeTextEl, bwidth;
3017
3018     
3019     // private
3020     var handleButton = function(button){
3021         dlg.hide();
3022         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3023     };
3024
3025     // private
3026     var handleHide = function(){
3027         if(opt && opt.cls){
3028             dlg.el.removeClass(opt.cls);
3029         }
3030         //if(waitTimer){
3031         //    Roo.TaskMgr.stop(waitTimer);
3032         //    waitTimer = null;
3033         //}
3034     };
3035
3036     // private
3037     var updateButtons = function(b){
3038         var width = 0;
3039         if(!b){
3040             buttons["ok"].hide();
3041             buttons["cancel"].hide();
3042             buttons["yes"].hide();
3043             buttons["no"].hide();
3044             //dlg.footer.dom.style.display = 'none';
3045             return width;
3046         }
3047         dlg.footerEl.dom.style.display = '';
3048         for(var k in buttons){
3049             if(typeof buttons[k] != "function"){
3050                 if(b[k]){
3051                     buttons[k].show();
3052                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3053                     width += buttons[k].el.getWidth()+15;
3054                 }else{
3055                     buttons[k].hide();
3056                 }
3057             }
3058         }
3059         return width;
3060     };
3061
3062     // private
3063     var handleEsc = function(d, k, e){
3064         if(opt && opt.closable !== false){
3065             dlg.hide();
3066         }
3067         if(e){
3068             e.stopEvent();
3069         }
3070     };
3071
3072     return {
3073         /**
3074          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3075          * @return {Roo.BasicDialog} The BasicDialog element
3076          */
3077         getDialog : function(){
3078            if(!dlg){
3079                 dlg = new Roo.bootstrap.Modal( {
3080                     //draggable: true,
3081                     //resizable:false,
3082                     //constraintoviewport:false,
3083                     //fixedcenter:true,
3084                     //collapsible : false,
3085                     //shim:true,
3086                     //modal: true,
3087                 //    width: 'auto',
3088                   //  height:100,
3089                     //buttonAlign:"center",
3090                     closeClick : function(){
3091                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3092                             handleButton("no");
3093                         }else{
3094                             handleButton("cancel");
3095                         }
3096                     }
3097                 });
3098                 dlg.render();
3099                 dlg.on("hide", handleHide);
3100                 mask = dlg.mask;
3101                 //dlg.addKeyListener(27, handleEsc);
3102                 buttons = {};
3103                 this.buttons = buttons;
3104                 var bt = this.buttonText;
3105                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3106                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3107                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3108                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3109                 //Roo.log(buttons);
3110                 bodyEl = dlg.bodyEl.createChild({
3111
3112                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3113                         '<textarea class="roo-mb-textarea"></textarea>' +
3114                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3115                 });
3116                 msgEl = bodyEl.dom.firstChild;
3117                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3118                 textboxEl.enableDisplayMode();
3119                 textboxEl.addKeyListener([10,13], function(){
3120                     if(dlg.isVisible() && opt && opt.buttons){
3121                         if(opt.buttons.ok){
3122                             handleButton("ok");
3123                         }else if(opt.buttons.yes){
3124                             handleButton("yes");
3125                         }
3126                     }
3127                 });
3128                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3129                 textareaEl.enableDisplayMode();
3130                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3131                 progressEl.enableDisplayMode();
3132                 
3133                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3134                 var pf = progressEl.dom.firstChild;
3135                 if (pf) {
3136                     pp = Roo.get(pf.firstChild);
3137                     pp.setHeight(pf.offsetHeight);
3138                 }
3139                 
3140             }
3141             return dlg;
3142         },
3143
3144         /**
3145          * Updates the message box body text
3146          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3147          * the XHTML-compliant non-breaking space character '&amp;#160;')
3148          * @return {Roo.MessageBox} This message box
3149          */
3150         updateText : function(text)
3151         {
3152             if(!dlg.isVisible() && !opt.width){
3153                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3154                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3155             }
3156             msgEl.innerHTML = text || '&#160;';
3157       
3158             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3159             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3160             var w = Math.max(
3161                     Math.min(opt.width || cw , this.maxWidth), 
3162                     Math.max(opt.minWidth || this.minWidth, bwidth)
3163             );
3164             if(opt.prompt){
3165                 activeTextEl.setWidth(w);
3166             }
3167             if(dlg.isVisible()){
3168                 dlg.fixedcenter = false;
3169             }
3170             // to big, make it scroll. = But as usual stupid IE does not support
3171             // !important..
3172             
3173             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3174                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3175                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3176             } else {
3177                 bodyEl.dom.style.height = '';
3178                 bodyEl.dom.style.overflowY = '';
3179             }
3180             if (cw > w) {
3181                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3182             } else {
3183                 bodyEl.dom.style.overflowX = '';
3184             }
3185             
3186             dlg.setContentSize(w, bodyEl.getHeight());
3187             if(dlg.isVisible()){
3188                 dlg.fixedcenter = true;
3189             }
3190             return this;
3191         },
3192
3193         /**
3194          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3195          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3196          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3197          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3198          * @return {Roo.MessageBox} This message box
3199          */
3200         updateProgress : function(value, text){
3201             if(text){
3202                 this.updateText(text);
3203             }
3204             
3205             if (pp) { // weird bug on my firefox - for some reason this is not defined
3206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3207                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3208             }
3209             return this;
3210         },        
3211
3212         /**
3213          * Returns true if the message box is currently displayed
3214          * @return {Boolean} True if the message box is visible, else false
3215          */
3216         isVisible : function(){
3217             return dlg && dlg.isVisible();  
3218         },
3219
3220         /**
3221          * Hides the message box if it is displayed
3222          */
3223         hide : function(){
3224             if(this.isVisible()){
3225                 dlg.hide();
3226             }  
3227         },
3228
3229         /**
3230          * Displays a new message box, or reinitializes an existing message box, based on the config options
3231          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3232          * The following config object properties are supported:
3233          * <pre>
3234 Property    Type             Description
3235 ----------  ---------------  ------------------------------------------------------------------------------------
3236 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3237                                    closes (defaults to undefined)
3238 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3239                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3240 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3241                                    progress and wait dialogs will ignore this property and always hide the
3242                                    close button as they can only be closed programmatically.
3243 cls               String           A custom CSS class to apply to the message box element
3244 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3245                                    displayed (defaults to 75)
3246 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3247                                    function will be btn (the name of the button that was clicked, if applicable,
3248                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3249                                    Progress and wait dialogs will ignore this option since they do not respond to
3250                                    user actions and can only be closed programmatically, so any required function
3251                                    should be called by the same code after it closes the dialog.
3252 icon              String           A CSS class that provides a background image to be used as an icon for
3253                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3254 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3255 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3256 modal             Boolean          False to allow user interaction with the page while the message box is
3257                                    displayed (defaults to true)
3258 msg               String           A string that will replace the existing message box body text (defaults
3259                                    to the XHTML-compliant non-breaking space character '&#160;')
3260 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3261 progress          Boolean          True to display a progress bar (defaults to false)
3262 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3263 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3264 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3265 title             String           The title text
3266 value             String           The string value to set into the active textbox element if displayed
3267 wait              Boolean          True to display a progress bar (defaults to false)
3268 width             Number           The width of the dialog in pixels
3269 </pre>
3270          *
3271          * Example usage:
3272          * <pre><code>
3273 Roo.Msg.show({
3274    title: 'Address',
3275    msg: 'Please enter your address:',
3276    width: 300,
3277    buttons: Roo.MessageBox.OKCANCEL,
3278    multiline: true,
3279    fn: saveAddress,
3280    animEl: 'addAddressBtn'
3281 });
3282 </code></pre>
3283          * @param {Object} config Configuration options
3284          * @return {Roo.MessageBox} This message box
3285          */
3286         show : function(options)
3287         {
3288             
3289             // this causes nightmares if you show one dialog after another
3290             // especially on callbacks..
3291              
3292             if(this.isVisible()){
3293                 
3294                 this.hide();
3295                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3296                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3297                 Roo.log("New Dialog Message:" +  options.msg )
3298                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3299                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3300                 
3301             }
3302             var d = this.getDialog();
3303             opt = options;
3304             d.setTitle(opt.title || "&#160;");
3305             d.closeEl.setDisplayed(opt.closable !== false);
3306             activeTextEl = textboxEl;
3307             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3308             if(opt.prompt){
3309                 if(opt.multiline){
3310                     textboxEl.hide();
3311                     textareaEl.show();
3312                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3313                         opt.multiline : this.defaultTextHeight);
3314                     activeTextEl = textareaEl;
3315                 }else{
3316                     textboxEl.show();
3317                     textareaEl.hide();
3318                 }
3319             }else{
3320                 textboxEl.hide();
3321                 textareaEl.hide();
3322             }
3323             progressEl.setDisplayed(opt.progress === true);
3324             this.updateProgress(0);
3325             activeTextEl.dom.value = opt.value || "";
3326             if(opt.prompt){
3327                 dlg.setDefaultButton(activeTextEl);
3328             }else{
3329                 var bs = opt.buttons;
3330                 var db = null;
3331                 if(bs && bs.ok){
3332                     db = buttons["ok"];
3333                 }else if(bs && bs.yes){
3334                     db = buttons["yes"];
3335                 }
3336                 dlg.setDefaultButton(db);
3337             }
3338             bwidth = updateButtons(opt.buttons);
3339             this.updateText(opt.msg);
3340             if(opt.cls){
3341                 d.el.addClass(opt.cls);
3342             }
3343             d.proxyDrag = opt.proxyDrag === true;
3344             d.modal = opt.modal !== false;
3345             d.mask = opt.modal !== false ? mask : false;
3346             if(!d.isVisible()){
3347                 // force it to the end of the z-index stack so it gets a cursor in FF
3348                 document.body.appendChild(dlg.el.dom);
3349                 d.animateTarget = null;
3350                 d.show(options.animEl);
3351             }
3352             return this;
3353         },
3354
3355         /**
3356          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3357          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3358          * and closing the message box when the process is complete.
3359          * @param {String} title The title bar text
3360          * @param {String} msg The message box body text
3361          * @return {Roo.MessageBox} This message box
3362          */
3363         progress : function(title, msg){
3364             this.show({
3365                 title : title,
3366                 msg : msg,
3367                 buttons: false,
3368                 progress:true,
3369                 closable:false,
3370                 minWidth: this.minProgressWidth,
3371                 modal : true
3372             });
3373             return this;
3374         },
3375
3376         /**
3377          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3378          * If a callback function is passed it will be called after the user clicks the button, and the
3379          * id of the button that was clicked will be passed as the only parameter to the callback
3380          * (could also be the top-right close button).
3381          * @param {String} title The title bar text
3382          * @param {String} msg The message box body text
3383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3384          * @param {Object} scope (optional) The scope of the callback function
3385          * @return {Roo.MessageBox} This message box
3386          */
3387         alert : function(title, msg, fn, scope)
3388         {
3389             this.show({
3390                 title : title,
3391                 msg : msg,
3392                 buttons: this.OK,
3393                 fn: fn,
3394                 closable : false,
3395                 scope : scope,
3396                 modal : true
3397             });
3398             return this;
3399         },
3400
3401         /**
3402          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3403          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3404          * You are responsible for closing the message box when the process is complete.
3405          * @param {String} msg The message box body text
3406          * @param {String} title (optional) The title bar text
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         wait : function(msg, title){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: false,
3414                 closable:false,
3415                 progress:true,
3416                 modal:true,
3417                 width:300,
3418                 wait:true
3419             });
3420             waitTimer = Roo.TaskMgr.start({
3421                 run: function(i){
3422                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3423                 },
3424                 interval: 1000
3425             });
3426             return this;
3427         },
3428
3429         /**
3430          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3431          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3432          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3433          * @param {String} title The title bar text
3434          * @param {String} msg The message box body text
3435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3436          * @param {Object} scope (optional) The scope of the callback function
3437          * @return {Roo.MessageBox} This message box
3438          */
3439         confirm : function(title, msg, fn, scope){
3440             this.show({
3441                 title : title,
3442                 msg : msg,
3443                 buttons: this.YESNO,
3444                 fn: fn,
3445                 scope : scope,
3446                 modal : true
3447             });
3448             return this;
3449         },
3450
3451         /**
3452          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3453          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3454          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3455          * (could also be the top-right close button) and the text that was entered will be passed as the two
3456          * parameters to the callback.
3457          * @param {String} title The title bar text
3458          * @param {String} msg The message box body text
3459          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3460          * @param {Object} scope (optional) The scope of the callback function
3461          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3462          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3463          * @return {Roo.MessageBox} This message box
3464          */
3465         prompt : function(title, msg, fn, scope, multiline){
3466             this.show({
3467                 title : title,
3468                 msg : msg,
3469                 buttons: this.OKCANCEL,
3470                 fn: fn,
3471                 minWidth:250,
3472                 scope : scope,
3473                 prompt:true,
3474                 multiline: multiline,
3475                 modal : true
3476             });
3477             return this;
3478         },
3479
3480         /**
3481          * Button config that displays a single OK button
3482          * @type Object
3483          */
3484         OK : {ok:true},
3485         /**
3486          * Button config that displays Yes and No buttons
3487          * @type Object
3488          */
3489         YESNO : {yes:true, no:true},
3490         /**
3491          * Button config that displays OK and Cancel buttons
3492          * @type Object
3493          */
3494         OKCANCEL : {ok:true, cancel:true},
3495         /**
3496          * Button config that displays Yes, No and Cancel buttons
3497          * @type Object
3498          */
3499         YESNOCANCEL : {yes:true, no:true, cancel:true},
3500
3501         /**
3502          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3503          * @type Number
3504          */
3505         defaultTextHeight : 75,
3506         /**
3507          * The maximum width in pixels of the message box (defaults to 600)
3508          * @type Number
3509          */
3510         maxWidth : 600,
3511         /**
3512          * The minimum width in pixels of the message box (defaults to 100)
3513          * @type Number
3514          */
3515         minWidth : 100,
3516         /**
3517          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3518          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3519          * @type Number
3520          */
3521         minProgressWidth : 250,
3522         /**
3523          * An object containing the default button text strings that can be overriden for localized language support.
3524          * Supported properties are: ok, cancel, yes and no.
3525          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3526          * @type Object
3527          */
3528         buttonText : {
3529             ok : "OK",
3530             cancel : "Cancel",
3531             yes : "Yes",
3532             no : "No"
3533         }
3534     };
3535 }();
3536
3537 /**
3538  * Shorthand for {@link Roo.MessageBox}
3539  */
3540 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3541 Roo.Msg = Roo.Msg || Roo.MessageBox;
3542 /*
3543  * - LGPL
3544  *
3545  * navbar
3546  * 
3547  */
3548
3549 /**
3550  * @class Roo.bootstrap.Navbar
3551  * @extends Roo.bootstrap.Component
3552  * Bootstrap Navbar class
3553
3554  * @constructor
3555  * Create a new Navbar
3556  * @param {Object} config The config object
3557  */
3558
3559
3560 Roo.bootstrap.Navbar = function(config){
3561     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3562     this.addEvents({
3563         // raw events
3564         /**
3565          * @event beforetoggle
3566          * Fire before toggle the menu
3567          * @param {Roo.EventObject} e
3568          */
3569         "beforetoggle" : true
3570     });
3571 };
3572
3573 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3574     
3575     
3576    
3577     // private
3578     navItems : false,
3579     loadMask : false,
3580     
3581     
3582     getAutoCreate : function(){
3583         
3584         
3585         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3586         
3587     },
3588     
3589     initEvents :function ()
3590     {
3591         //Roo.log(this.el.select('.navbar-toggle',true));
3592         this.el.select('.navbar-toggle',true).on('click', function() {
3593             if(this.fireEvent('beforetoggle', this) !== false){
3594                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3595             }
3596             
3597         }, this);
3598         
3599         var mark = {
3600             tag: "div",
3601             cls:"x-dlg-mask"
3602         };
3603         
3604         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3605         
3606         var size = this.el.getSize();
3607         this.maskEl.setSize(size.width, size.height);
3608         this.maskEl.enableDisplayMode("block");
3609         this.maskEl.hide();
3610         
3611         if(this.loadMask){
3612             this.maskEl.show();
3613         }
3614     },
3615     
3616     
3617     getChildContainer : function()
3618     {
3619         if (this.el.select('.collapse').getCount()) {
3620             return this.el.select('.collapse',true).first();
3621         }
3622         
3623         return this.el;
3624     },
3625     
3626     mask : function()
3627     {
3628         this.maskEl.show();
3629     },
3630     
3631     unmask : function()
3632     {
3633         this.maskEl.hide();
3634     } 
3635     
3636     
3637     
3638     
3639 });
3640
3641
3642
3643  
3644
3645  /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSimplebar
3654  * @extends Roo.bootstrap.Navbar
3655  * Bootstrap Sidebar class
3656  *
3657  * @cfg {Boolean} inverse is inverted color
3658  * 
3659  * @cfg {String} type (nav | pills | tabs)
3660  * @cfg {Boolean} arrangement stacked | justified
3661  * @cfg {String} align (left | right) alignment
3662  * 
3663  * @cfg {Boolean} main (true|false) main nav bar? default false
3664  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3665  * 
3666  * @cfg {String} tag (header|footer|nav|div) default is nav 
3667
3668  * 
3669  * 
3670  * 
3671  * @constructor
3672  * Create a new Sidebar
3673  * @param {Object} config The config object
3674  */
3675
3676
3677 Roo.bootstrap.NavSimplebar = function(config){
3678     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3682     
3683     inverse: false,
3684     
3685     type: false,
3686     arrangement: '',
3687     align : false,
3688     
3689     
3690     
3691     main : false,
3692     
3693     
3694     tag : false,
3695     
3696     
3697     getAutoCreate : function(){
3698         
3699         
3700         var cfg = {
3701             tag : this.tag || 'div',
3702             cls : 'navbar'
3703         };
3704           
3705         
3706         cfg.cn = [
3707             {
3708                 cls: 'nav',
3709                 tag : 'ul'
3710             }
3711         ];
3712         
3713          
3714         this.type = this.type || 'nav';
3715         if (['tabs','pills'].indexOf(this.type)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.type
3717         
3718         
3719         } else {
3720             if (this.type!=='nav') {
3721                 Roo.log('nav type must be nav/tabs/pills')
3722             }
3723             cfg.cn[0].cls += ' navbar-nav'
3724         }
3725         
3726         
3727         
3728         
3729         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3730             cfg.cn[0].cls += ' nav-' + this.arrangement;
3731         }
3732         
3733         
3734         if (this.align === 'right') {
3735             cfg.cn[0].cls += ' navbar-right';
3736         }
3737         
3738         if (this.inverse) {
3739             cfg.cls += ' navbar-inverse';
3740             
3741         }
3742         
3743         
3744         return cfg;
3745     
3746         
3747     }
3748     
3749     
3750     
3751 });
3752
3753
3754
3755  
3756
3757  
3758        /*
3759  * - LGPL
3760  *
3761  * navbar
3762  * 
3763  */
3764
3765 /**
3766  * @class Roo.bootstrap.NavHeaderbar
3767  * @extends Roo.bootstrap.NavSimplebar
3768  * Bootstrap Sidebar class
3769  *
3770  * @cfg {String} brand what is brand
3771  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3772  * @cfg {String} brand_href href of the brand
3773  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3774  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3775  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3776  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3777  * 
3778  * @constructor
3779  * Create a new Sidebar
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.NavHeaderbar = function(config){
3785     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3786       
3787 };
3788
3789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3790     
3791     position: '',
3792     brand: '',
3793     brand_href: false,
3794     srButton : true,
3795     autohide : false,
3796     desktopCenter : false,
3797    
3798     
3799     getAutoCreate : function(){
3800         
3801         var   cfg = {
3802             tag: this.nav || 'nav',
3803             cls: 'navbar',
3804             role: 'navigation',
3805             cn: []
3806         };
3807         
3808         var cn = cfg.cn;
3809         if (this.desktopCenter) {
3810             cn.push({cls : 'container', cn : []});
3811             cn = cn[0].cn;
3812         }
3813         
3814         if(this.srButton){
3815             cn.push({
3816                 tag: 'div',
3817                 cls: 'navbar-header',
3818                 cn: [
3819                     {
3820                         tag: 'button',
3821                         type: 'button',
3822                         cls: 'navbar-toggle',
3823                         'data-toggle': 'collapse',
3824                         cn: [
3825                             {
3826                                 tag: 'span',
3827                                 cls: 'sr-only',
3828                                 html: 'Toggle navigation'
3829                             },
3830                             {
3831                                 tag: 'span',
3832                                 cls: 'icon-bar'
3833                             },
3834                             {
3835                                 tag: 'span',
3836                                 cls: 'icon-bar'
3837                             },
3838                             {
3839                                 tag: 'span',
3840                                 cls: 'icon-bar'
3841                             }
3842                         ]
3843                     }
3844                 ]
3845             });
3846         }
3847         
3848         cn.push({
3849             tag: 'div',
3850             cls: 'collapse navbar-collapse',
3851             cn : []
3852         });
3853         
3854         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3855         
3856         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3857             cfg.cls += ' navbar-' + this.position;
3858             
3859             // tag can override this..
3860             
3861             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3862         }
3863         
3864         if (this.brand !== '') {
3865             cn[0].cn.push({
3866                 tag: 'a',
3867                 href: this.brand_href ? this.brand_href : '#',
3868                 cls: 'navbar-brand',
3869                 cn: [
3870                 this.brand
3871                 ]
3872             });
3873         }
3874         
3875         if(this.main){
3876             cfg.cls += ' main-nav';
3877         }
3878         
3879         
3880         return cfg;
3881
3882         
3883     },
3884     getHeaderChildContainer : function()
3885     {
3886         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3887             return this.el.select('.navbar-header',true).first();
3888         }
3889         
3890         return this.getChildContainer();
3891     },
3892     
3893     
3894     initEvents : function()
3895     {
3896         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3897         
3898         if (this.autohide) {
3899             
3900             var prevScroll = 0;
3901             var ft = this.el;
3902             
3903             Roo.get(document).on('scroll',function(e) {
3904                 var ns = Roo.get(document).getScroll().top;
3905                 var os = prevScroll;
3906                 prevScroll = ns;
3907                 
3908                 if(ns > os){
3909                     ft.removeClass('slideDown');
3910                     ft.addClass('slideUp');
3911                     return;
3912                 }
3913                 ft.removeClass('slideUp');
3914                 ft.addClass('slideDown');
3915                  
3916               
3917           },this);
3918         }
3919     }    
3920     
3921 });
3922
3923
3924
3925  
3926
3927  /*
3928  * - LGPL
3929  *
3930  * navbar
3931  * 
3932  */
3933
3934 /**
3935  * @class Roo.bootstrap.NavSidebar
3936  * @extends Roo.bootstrap.Navbar
3937  * Bootstrap Sidebar class
3938  * 
3939  * @constructor
3940  * Create a new Sidebar
3941  * @param {Object} config The config object
3942  */
3943
3944
3945 Roo.bootstrap.NavSidebar = function(config){
3946     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3947 };
3948
3949 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3950     
3951     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3952     
3953     getAutoCreate : function(){
3954         
3955         
3956         return  {
3957             tag: 'div',
3958             cls: 'sidebar sidebar-nav'
3959         };
3960     
3961         
3962     }
3963     
3964     
3965     
3966 });
3967
3968
3969
3970  
3971
3972  /*
3973  * - LGPL
3974  *
3975  * nav group
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavGroup
3981  * @extends Roo.bootstrap.Component
3982  * Bootstrap NavGroup class
3983  * @cfg {String} align (left|right)
3984  * @cfg {Boolean} inverse
3985  * @cfg {String} type (nav|pills|tab) default nav
3986  * @cfg {String} navId - reference Id for navbar.
3987
3988  * 
3989  * @constructor
3990  * Create a new nav group
3991  * @param {Object} config The config object
3992  */
3993
3994 Roo.bootstrap.NavGroup = function(config){
3995     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3996     this.navItems = [];
3997    
3998     Roo.bootstrap.NavGroup.register(this);
3999      this.addEvents({
4000         /**
4001              * @event changed
4002              * Fires when the active item changes
4003              * @param {Roo.bootstrap.NavGroup} this
4004              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4005              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4006          */
4007         'changed': true
4008      });
4009     
4010 };
4011
4012 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4013     
4014     align: '',
4015     inverse: false,
4016     form: false,
4017     type: 'nav',
4018     navId : '',
4019     // private
4020     
4021     navItems : false, 
4022     
4023     getAutoCreate : function()
4024     {
4025         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4026         
4027         cfg = {
4028             tag : 'ul',
4029             cls: 'nav' 
4030         };
4031         
4032         if (['tabs','pills'].indexOf(this.type)!==-1) {
4033             cfg.cls += ' nav-' + this.type
4034         } else {
4035             if (this.type!=='nav') {
4036                 Roo.log('nav type must be nav/tabs/pills')
4037             }
4038             cfg.cls += ' navbar-nav'
4039         }
4040         
4041         if (this.parent() && this.parent().sidebar) {
4042             cfg = {
4043                 tag: 'ul',
4044                 cls: 'dashboard-menu sidebar-menu'
4045             };
4046             
4047             return cfg;
4048         }
4049         
4050         if (this.form === true) {
4051             cfg = {
4052                 tag: 'form',
4053                 cls: 'navbar-form'
4054             };
4055             
4056             if (this.align === 'right') {
4057                 cfg.cls += ' navbar-right';
4058             } else {
4059                 cfg.cls += ' navbar-left';
4060             }
4061         }
4062         
4063         if (this.align === 'right') {
4064             cfg.cls += ' navbar-right';
4065         }
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         
4073         return cfg;
4074     },
4075     /**
4076     * sets the active Navigation item
4077     * @param {Roo.bootstrap.NavItem} the new current navitem
4078     */
4079     setActiveItem : function(item)
4080     {
4081         var prev = false;
4082         Roo.each(this.navItems, function(v){
4083             if (v == item) {
4084                 return ;
4085             }
4086             if (v.isActive()) {
4087                 v.setActive(false, true);
4088                 prev = v;
4089                 
4090             }
4091             
4092         });
4093
4094         item.setActive(true, true);
4095         this.fireEvent('changed', this, item, prev);
4096         
4097         
4098     },
4099     /**
4100     * gets the active Navigation item
4101     * @return {Roo.bootstrap.NavItem} the current navitem
4102     */
4103     getActive : function()
4104     {
4105         
4106         var prev = false;
4107         Roo.each(this.navItems, function(v){
4108             
4109             if (v.isActive()) {
4110                 prev = v;
4111                 
4112             }
4113             
4114         });
4115         return prev;
4116     },
4117     
4118     indexOfNav : function()
4119     {
4120         
4121         var prev = false;
4122         Roo.each(this.navItems, function(v,i){
4123             
4124             if (v.isActive()) {
4125                 prev = i;
4126                 
4127             }
4128             
4129         });
4130         return prev;
4131     },
4132     /**
4133     * adds a Navigation item
4134     * @param {Roo.bootstrap.NavItem} the navitem to add
4135     */
4136     addItem : function(cfg)
4137     {
4138         var cn = new Roo.bootstrap.NavItem(cfg);
4139         this.register(cn);
4140         cn.parentId = this.id;
4141         cn.onRender(this.el, null);
4142         return cn;
4143     },
4144     /**
4145     * register a Navigation item
4146     * @param {Roo.bootstrap.NavItem} the navitem to add
4147     */
4148     register : function(item)
4149     {
4150         this.navItems.push( item);
4151         item.navId = this.navId;
4152     
4153     },
4154     
4155     /**
4156     * clear all the Navigation item
4157     */
4158    
4159     clearAll : function()
4160     {
4161         this.navItems = [];
4162         this.el.dom.innerHTML = '';
4163     },
4164     
4165     getNavItem: function(tabId)
4166     {
4167         var ret = false;
4168         Roo.each(this.navItems, function(e) {
4169             if (e.tabId == tabId) {
4170                ret =  e;
4171                return false;
4172             }
4173             return true;
4174             
4175         });
4176         return ret;
4177     },
4178     
4179     setActiveNext : function()
4180     {
4181         var i = this.indexOfNav(this.getActive());
4182         if (i > this.navItems.length) {
4183             return;
4184         }
4185         this.setActiveItem(this.navItems[i+1]);
4186     },
4187     setActivePrev : function()
4188     {
4189         var i = this.indexOfNav(this.getActive());
4190         if (i  < 1) {
4191             return;
4192         }
4193         this.setActiveItem(this.navItems[i-1]);
4194     },
4195     clearWasActive : function(except) {
4196         Roo.each(this.navItems, function(e) {
4197             if (e.tabId != except.tabId && e.was_active) {
4198                e.was_active = false;
4199                return false;
4200             }
4201             return true;
4202             
4203         });
4204     },
4205     getWasActive : function ()
4206     {
4207         var r = false;
4208         Roo.each(this.navItems, function(e) {
4209             if (e.was_active) {
4210                r = e;
4211                return false;
4212             }
4213             return true;
4214             
4215         });
4216         return r;
4217     }
4218     
4219     
4220 });
4221
4222  
4223 Roo.apply(Roo.bootstrap.NavGroup, {
4224     
4225     groups: {},
4226      /**
4227     * register a Navigation Group
4228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4229     */
4230     register : function(navgrp)
4231     {
4232         this.groups[navgrp.navId] = navgrp;
4233         
4234     },
4235     /**
4236     * fetch a Navigation Group based on the navigation ID
4237     * @param {string} the navgroup to add
4238     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4239     */
4240     get: function(navId) {
4241         if (typeof(this.groups[navId]) == 'undefined') {
4242             return false;
4243             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4244         }
4245         return this.groups[navId] ;
4246     }
4247     
4248     
4249     
4250 });
4251
4252  /*
4253  * - LGPL
4254  *
4255  * row
4256  * 
4257  */
4258
4259 /**
4260  * @class Roo.bootstrap.NavItem
4261  * @extends Roo.bootstrap.Component
4262  * Bootstrap Navbar.NavItem class
4263  * @cfg {String} href  link to
4264  * @cfg {String} html content of button
4265  * @cfg {String} badge text inside badge
4266  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4267  * @cfg {String} glyphicon name of glyphicon
4268  * @cfg {String} icon name of font awesome icon
4269  * @cfg {Boolean} active Is item active
4270  * @cfg {Boolean} disabled Is item disabled
4271  
4272  * @cfg {Boolean} preventDefault (true | false) default false
4273  * @cfg {String} tabId the tab that this item activates.
4274  * @cfg {String} tagtype (a|span) render as a href or span?
4275  * @cfg {Boolean} animateRef (true|false) link to element default false  
4276   
4277  * @constructor
4278  * Create a new Navbar Item
4279  * @param {Object} config The config object
4280  */
4281 Roo.bootstrap.NavItem = function(config){
4282     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4283     this.addEvents({
4284         // raw events
4285         /**
4286          * @event click
4287          * The raw click event for the entire grid.
4288          * @param {Roo.EventObject} e
4289          */
4290         "click" : true,
4291          /**
4292             * @event changed
4293             * Fires when the active item active state changes
4294             * @param {Roo.bootstrap.NavItem} this
4295             * @param {boolean} state the new state
4296              
4297          */
4298         'changed': true,
4299         /**
4300             * @event scrollto
4301             * Fires when scroll to element
4302             * @param {Roo.bootstrap.NavItem} this
4303             * @param {Object} options
4304             * @param {Roo.EventObject} e
4305              
4306          */
4307         'scrollto': true
4308     });
4309    
4310 };
4311
4312 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4313     
4314     href: false,
4315     html: '',
4316     badge: '',
4317     icon: false,
4318     glyphicon: false,
4319     active: false,
4320     preventDefault : false,
4321     tabId : false,
4322     tagtype : 'a',
4323     disabled : false,
4324     animateRef : false,
4325     was_active : false,
4326     
4327     getAutoCreate : function(){
4328          
4329         var cfg = {
4330             tag: 'li',
4331             cls: 'nav-item'
4332             
4333         };
4334         
4335         if (this.active) {
4336             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4337         }
4338         if (this.disabled) {
4339             cfg.cls += ' disabled';
4340         }
4341         
4342         if (this.href || this.html || this.glyphicon || this.icon) {
4343             cfg.cn = [
4344                 {
4345                     tag: this.tagtype,
4346                     href : this.href || "#",
4347                     html: this.html || ''
4348                 }
4349             ];
4350             
4351             if (this.icon) {
4352                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4353             }
4354
4355             if(this.glyphicon) {
4356                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4357             }
4358             
4359             if (this.menu) {
4360                 
4361                 cfg.cn[0].html += " <span class='caret'></span>";
4362              
4363             }
4364             
4365             if (this.badge !== '') {
4366                  
4367                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4368             }
4369         }
4370         
4371         
4372         
4373         return cfg;
4374     },
4375     initEvents: function() 
4376     {
4377         if (typeof (this.menu) != 'undefined') {
4378             this.menu.parentType = this.xtype;
4379             this.menu.triggerEl = this.el;
4380             this.menu = this.addxtype(Roo.apply({}, this.menu));
4381         }
4382         
4383         this.el.select('a',true).on('click', this.onClick, this);
4384         
4385         if(this.tagtype == 'span'){
4386             this.el.select('span',true).on('click', this.onClick, this);
4387         }
4388        
4389         // at this point parent should be available..
4390         this.parent().register(this);
4391     },
4392     
4393     onClick : function(e)
4394     {
4395         if (e.getTarget('.dropdown-menu-item')) {
4396             // did you click on a menu itemm.... - then don't trigger onclick..
4397             return;
4398         }
4399         
4400         if(
4401                 this.preventDefault || 
4402                 this.href == '#' 
4403         ){
4404             Roo.log("NavItem - prevent Default?");
4405             e.preventDefault();
4406         }
4407         
4408         if (this.disabled) {
4409             return;
4410         }
4411         
4412         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4413         if (tg && tg.transition) {
4414             Roo.log("waiting for the transitionend");
4415             return;
4416         }
4417         
4418         
4419         
4420         //Roo.log("fire event clicked");
4421         if(this.fireEvent('click', this, e) === false){
4422             return;
4423         };
4424         
4425         if(this.tagtype == 'span'){
4426             return;
4427         }
4428         
4429         //Roo.log(this.href);
4430         var ael = this.el.select('a',true).first();
4431         //Roo.log(ael);
4432         
4433         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4434             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4435             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4436                 return; // ignore... - it's a 'hash' to another page.
4437             }
4438             Roo.log("NavItem - prevent Default?");
4439             e.preventDefault();
4440             this.scrollToElement(e);
4441         }
4442         
4443         
4444         var p =  this.parent();
4445    
4446         if (['tabs','pills'].indexOf(p.type)!==-1) {
4447             if (typeof(p.setActiveItem) !== 'undefined') {
4448                 p.setActiveItem(this);
4449             }
4450         }
4451         
4452         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4453         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4454             // remove the collapsed menu expand...
4455             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4456         }
4457     },
4458     
4459     isActive: function () {
4460         return this.active
4461     },
4462     setActive : function(state, fire, is_was_active)
4463     {
4464         if (this.active && !state && this.navId) {
4465             this.was_active = true;
4466             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4467             if (nv) {
4468                 nv.clearWasActive(this);
4469             }
4470             
4471         }
4472         this.active = state;
4473         
4474         if (!state ) {
4475             this.el.removeClass('active');
4476         } else if (!this.el.hasClass('active')) {
4477             this.el.addClass('active');
4478         }
4479         if (fire) {
4480             this.fireEvent('changed', this, state);
4481         }
4482         
4483         // show a panel if it's registered and related..
4484         
4485         if (!this.navId || !this.tabId || !state || is_was_active) {
4486             return;
4487         }
4488         
4489         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4490         if (!tg) {
4491             return;
4492         }
4493         var pan = tg.getPanelByName(this.tabId);
4494         if (!pan) {
4495             return;
4496         }
4497         // if we can not flip to new panel - go back to old nav highlight..
4498         if (false == tg.showPanel(pan)) {
4499             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4500             if (nv) {
4501                 var onav = nv.getWasActive();
4502                 if (onav) {
4503                     onav.setActive(true, false, true);
4504                 }
4505             }
4506             
4507         }
4508         
4509         
4510         
4511     },
4512      // this should not be here...
4513     setDisabled : function(state)
4514     {
4515         this.disabled = state;
4516         if (!state ) {
4517             this.el.removeClass('disabled');
4518         } else if (!this.el.hasClass('disabled')) {
4519             this.el.addClass('disabled');
4520         }
4521         
4522     },
4523     
4524     /**
4525      * Fetch the element to display the tooltip on.
4526      * @return {Roo.Element} defaults to this.el
4527      */
4528     tooltipEl : function()
4529     {
4530         return this.el.select('' + this.tagtype + '', true).first();
4531     },
4532     
4533     scrollToElement : function(e)
4534     {
4535         var c = document.body;
4536         
4537         /*
4538          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4539          */
4540         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4541             c = document.documentElement;
4542         }
4543         
4544         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4545         
4546         if(!target){
4547             return;
4548         }
4549
4550         var o = target.calcOffsetsTo(c);
4551         
4552         var options = {
4553             target : target,
4554             value : o[1]
4555         };
4556         
4557         this.fireEvent('scrollto', this, options, e);
4558         
4559         Roo.get(c).scrollTo('top', options.value, true);
4560         
4561         return;
4562     }
4563 });
4564  
4565
4566  /*
4567  * - LGPL
4568  *
4569  * sidebar item
4570  *
4571  *  li
4572  *    <span> icon </span>
4573  *    <span> text </span>
4574  *    <span>badge </span>
4575  */
4576
4577 /**
4578  * @class Roo.bootstrap.NavSidebarItem
4579  * @extends Roo.bootstrap.NavItem
4580  * Bootstrap Navbar.NavSidebarItem class
4581  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4582  * {Boolean} open is the menu open
4583  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4584  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4585  * {String} buttonSize (sm|md|lg)the extra classes for the button
4586  * {Boolean} showArrow show arrow next to the text (default true)
4587  * @constructor
4588  * Create a new Navbar Button
4589  * @param {Object} config The config object
4590  */
4591 Roo.bootstrap.NavSidebarItem = function(config){
4592     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4593     this.addEvents({
4594         // raw events
4595         /**
4596          * @event click
4597          * The raw click event for the entire grid.
4598          * @param {Roo.EventObject} e
4599          */
4600         "click" : true,
4601          /**
4602             * @event changed
4603             * Fires when the active item active state changes
4604             * @param {Roo.bootstrap.NavSidebarItem} this
4605             * @param {boolean} state the new state
4606              
4607          */
4608         'changed': true
4609     });
4610    
4611 };
4612
4613 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4614     
4615     badgeWeight : 'default',
4616     
4617     open: false,
4618     
4619     buttonView : false,
4620     
4621     buttonWeight : 'default',
4622     
4623     buttonSize : 'md',
4624     
4625     showArrow : true,
4626     
4627     getAutoCreate : function(){
4628         
4629         
4630         var a = {
4631                 tag: 'a',
4632                 href : this.href || '#',
4633                 cls: '',
4634                 html : '',
4635                 cn : []
4636         };
4637         
4638         if(this.buttonView){
4639             a = {
4640                 tag: 'button',
4641                 href : this.href || '#',
4642                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4643                 html : this.html,
4644                 cn : []
4645             };
4646         }
4647         
4648         var cfg = {
4649             tag: 'li',
4650             cls: '',
4651             cn: [ a ]
4652         };
4653         
4654         if (this.active) {
4655             cfg.cls += ' active';
4656         }
4657         
4658         if (this.disabled) {
4659             cfg.cls += ' disabled';
4660         }
4661         if (this.open) {
4662             cfg.cls += ' open x-open';
4663         }
4664         // left icon..
4665         if (this.glyphicon || this.icon) {
4666             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4667             a.cn.push({ tag : 'i', cls : c }) ;
4668         }
4669         
4670         if(!this.buttonView){
4671             var span = {
4672                 tag: 'span',
4673                 html : this.html || ''
4674             };
4675
4676             a.cn.push(span);
4677             
4678         }
4679         
4680         if (this.badge !== '') {
4681             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4682         }
4683         
4684         if (this.menu) {
4685             
4686             if(this.showArrow){
4687                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4688             }
4689             
4690             a.cls += ' dropdown-toggle treeview' ;
4691         }
4692         
4693         return cfg;
4694     },
4695     
4696     initEvents : function()
4697     { 
4698         if (typeof (this.menu) != 'undefined') {
4699             this.menu.parentType = this.xtype;
4700             this.menu.triggerEl = this.el;
4701             this.menu = this.addxtype(Roo.apply({}, this.menu));
4702         }
4703         
4704         this.el.on('click', this.onClick, this);
4705         
4706         if(this.badge !== ''){
4707             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4708         }
4709         
4710     },
4711     
4712     onClick : function(e)
4713     {
4714         if(this.disabled){
4715             e.preventDefault();
4716             return;
4717         }
4718         
4719         if(this.preventDefault){
4720             e.preventDefault();
4721         }
4722         
4723         this.fireEvent('click', this);
4724     },
4725     
4726     disable : function()
4727     {
4728         this.setDisabled(true);
4729     },
4730     
4731     enable : function()
4732     {
4733         this.setDisabled(false);
4734     },
4735     
4736     setDisabled : function(state)
4737     {
4738         if(this.disabled == state){
4739             return;
4740         }
4741         
4742         this.disabled = state;
4743         
4744         if (state) {
4745             this.el.addClass('disabled');
4746             return;
4747         }
4748         
4749         this.el.removeClass('disabled');
4750         
4751         return;
4752     },
4753     
4754     setActive : function(state)
4755     {
4756         if(this.active == state){
4757             return;
4758         }
4759         
4760         this.active = state;
4761         
4762         if (state) {
4763             this.el.addClass('active');
4764             return;
4765         }
4766         
4767         this.el.removeClass('active');
4768         
4769         return;
4770     },
4771     
4772     isActive: function () 
4773     {
4774         return this.active;
4775     },
4776     
4777     setBadge : function(str)
4778     {
4779         if(!this.badgeEl){
4780             return;
4781         }
4782         
4783         this.badgeEl.dom.innerHTML = str;
4784     }
4785     
4786    
4787      
4788  
4789 });
4790  
4791
4792  /*
4793  * - LGPL
4794  *
4795  * row
4796  * 
4797  */
4798
4799 /**
4800  * @class Roo.bootstrap.Row
4801  * @extends Roo.bootstrap.Component
4802  * Bootstrap Row class (contains columns...)
4803  * 
4804  * @constructor
4805  * Create a new Row
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Row = function(config){
4810     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4811 };
4812
4813 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4814     
4815     getAutoCreate : function(){
4816        return {
4817             cls: 'row clearfix'
4818        };
4819     }
4820     
4821     
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * element
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Element
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Element class
4837  * @cfg {String} html contents of the element
4838  * @cfg {String} tag tag of the element
4839  * @cfg {String} cls class of the element
4840  * @cfg {Boolean} preventDefault (true|false) default false
4841  * @cfg {Boolean} clickable (true|false) default false
4842  * 
4843  * @constructor
4844  * Create a new Element
4845  * @param {Object} config The config object
4846  */
4847
4848 Roo.bootstrap.Element = function(config){
4849     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4850     
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * When a element is chick
4856          * @param {Roo.bootstrap.Element} this
4857          * @param {Roo.EventObject} e
4858          */
4859         "click" : true
4860     });
4861 };
4862
4863 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4864     
4865     tag: 'div',
4866     cls: '',
4867     html: '',
4868     preventDefault: false, 
4869     clickable: false,
4870     
4871     getAutoCreate : function(){
4872         
4873         var cfg = {
4874             tag: this.tag,
4875             cls: this.cls,
4876             html: this.html
4877         };
4878         
4879         return cfg;
4880     },
4881     
4882     initEvents: function() 
4883     {
4884         Roo.bootstrap.Element.superclass.initEvents.call(this);
4885         
4886         if(this.clickable){
4887             this.el.on('click', this.onClick, this);
4888         }
4889         
4890     },
4891     
4892     onClick : function(e)
4893     {
4894         if(this.preventDefault){
4895             e.preventDefault();
4896         }
4897         
4898         this.fireEvent('click', this, e);
4899     },
4900     
4901     getValue : function()
4902     {
4903         return this.el.dom.innerHTML;
4904     },
4905     
4906     setValue : function(value)
4907     {
4908         this.el.dom.innerHTML = value;
4909     }
4910    
4911 });
4912
4913  
4914
4915  /*
4916  * - LGPL
4917  *
4918  * pagination
4919  * 
4920  */
4921
4922 /**
4923  * @class Roo.bootstrap.Pagination
4924  * @extends Roo.bootstrap.Component
4925  * Bootstrap Pagination class
4926  * @cfg {String} size xs | sm | md | lg
4927  * @cfg {Boolean} inverse false | true
4928  * 
4929  * @constructor
4930  * Create a new Pagination
4931  * @param {Object} config The config object
4932  */
4933
4934 Roo.bootstrap.Pagination = function(config){
4935     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4936 };
4937
4938 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4939     
4940     cls: false,
4941     size: false,
4942     inverse: false,
4943     
4944     getAutoCreate : function(){
4945         var cfg = {
4946             tag: 'ul',
4947                 cls: 'pagination'
4948         };
4949         if (this.inverse) {
4950             cfg.cls += ' inverse';
4951         }
4952         if (this.html) {
4953             cfg.html=this.html;
4954         }
4955         if (this.cls) {
4956             cfg.cls += " " + this.cls;
4957         }
4958         return cfg;
4959     }
4960    
4961 });
4962
4963  
4964
4965  /*
4966  * - LGPL
4967  *
4968  * Pagination item
4969  * 
4970  */
4971
4972
4973 /**
4974  * @class Roo.bootstrap.PaginationItem
4975  * @extends Roo.bootstrap.Component
4976  * Bootstrap PaginationItem class
4977  * @cfg {String} html text
4978  * @cfg {String} href the link
4979  * @cfg {Boolean} preventDefault (true | false) default true
4980  * @cfg {Boolean} active (true | false) default false
4981  * @cfg {Boolean} disabled default false
4982  * 
4983  * 
4984  * @constructor
4985  * Create a new PaginationItem
4986  * @param {Object} config The config object
4987  */
4988
4989
4990 Roo.bootstrap.PaginationItem = function(config){
4991     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4992     this.addEvents({
4993         // raw events
4994         /**
4995          * @event click
4996          * The raw click event for the entire grid.
4997          * @param {Roo.EventObject} e
4998          */
4999         "click" : true
5000     });
5001 };
5002
5003 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5004     
5005     href : false,
5006     html : false,
5007     preventDefault: true,
5008     active : false,
5009     cls : false,
5010     disabled: false,
5011     
5012     getAutoCreate : function(){
5013         var cfg= {
5014             tag: 'li',
5015             cn: [
5016                 {
5017                     tag : 'a',
5018                     href : this.href ? this.href : '#',
5019                     html : this.html ? this.html : ''
5020                 }
5021             ]
5022         };
5023         
5024         if(this.cls){
5025             cfg.cls = this.cls;
5026         }
5027         
5028         if(this.disabled){
5029             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5030         }
5031         
5032         if(this.active){
5033             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5034         }
5035         
5036         return cfg;
5037     },
5038     
5039     initEvents: function() {
5040         
5041         this.el.on('click', this.onClick, this);
5042         
5043     },
5044     onClick : function(e)
5045     {
5046         Roo.log('PaginationItem on click ');
5047         if(this.preventDefault){
5048             e.preventDefault();
5049         }
5050         
5051         if(this.disabled){
5052             return;
5053         }
5054         
5055         this.fireEvent('click', this, e);
5056     }
5057    
5058 });
5059
5060  
5061
5062  /*
5063  * - LGPL
5064  *
5065  * slider
5066  * 
5067  */
5068
5069
5070 /**
5071  * @class Roo.bootstrap.Slider
5072  * @extends Roo.bootstrap.Component
5073  * Bootstrap Slider class
5074  *    
5075  * @constructor
5076  * Create a new Slider
5077  * @param {Object} config The config object
5078  */
5079
5080 Roo.bootstrap.Slider = function(config){
5081     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5082 };
5083
5084 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5085     
5086     getAutoCreate : function(){
5087         
5088         var cfg = {
5089             tag: 'div',
5090             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5091             cn: [
5092                 {
5093                     tag: 'a',
5094                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5095                 }
5096             ]
5097         };
5098         
5099         return cfg;
5100     }
5101    
5102 });
5103
5104  /*
5105  * Based on:
5106  * Ext JS Library 1.1.1
5107  * Copyright(c) 2006-2007, Ext JS, LLC.
5108  *
5109  * Originally Released Under LGPL - original licence link has changed is not relivant.
5110  *
5111  * Fork - LGPL
5112  * <script type="text/javascript">
5113  */
5114  
5115
5116 /**
5117  * @class Roo.grid.ColumnModel
5118  * @extends Roo.util.Observable
5119  * This is the default implementation of a ColumnModel used by the Grid. It defines
5120  * the columns in the grid.
5121  * <br>Usage:<br>
5122  <pre><code>
5123  var colModel = new Roo.grid.ColumnModel([
5124         {header: "Ticker", width: 60, sortable: true, locked: true},
5125         {header: "Company Name", width: 150, sortable: true},
5126         {header: "Market Cap.", width: 100, sortable: true},
5127         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5128         {header: "Employees", width: 100, sortable: true, resizable: false}
5129  ]);
5130  </code></pre>
5131  * <p>
5132  
5133  * The config options listed for this class are options which may appear in each
5134  * individual column definition.
5135  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5136  * @constructor
5137  * @param {Object} config An Array of column config objects. See this class's
5138  * config objects for details.
5139 */
5140 Roo.grid.ColumnModel = function(config){
5141         /**
5142      * The config passed into the constructor
5143      */
5144     this.config = config;
5145     this.lookup = {};
5146
5147     // if no id, create one
5148     // if the column does not have a dataIndex mapping,
5149     // map it to the order it is in the config
5150     for(var i = 0, len = config.length; i < len; i++){
5151         var c = config[i];
5152         if(typeof c.dataIndex == "undefined"){
5153             c.dataIndex = i;
5154         }
5155         if(typeof c.renderer == "string"){
5156             c.renderer = Roo.util.Format[c.renderer];
5157         }
5158         if(typeof c.id == "undefined"){
5159             c.id = Roo.id();
5160         }
5161         if(c.editor && c.editor.xtype){
5162             c.editor  = Roo.factory(c.editor, Roo.grid);
5163         }
5164         if(c.editor && c.editor.isFormField){
5165             c.editor = new Roo.grid.GridEditor(c.editor);
5166         }
5167         this.lookup[c.id] = c;
5168     }
5169
5170     /**
5171      * The width of columns which have no width specified (defaults to 100)
5172      * @type Number
5173      */
5174     this.defaultWidth = 100;
5175
5176     /**
5177      * Default sortable of columns which have no sortable specified (defaults to false)
5178      * @type Boolean
5179      */
5180     this.defaultSortable = false;
5181
5182     this.addEvents({
5183         /**
5184              * @event widthchange
5185              * Fires when the width of a column changes.
5186              * @param {ColumnModel} this
5187              * @param {Number} columnIndex The column index
5188              * @param {Number} newWidth The new width
5189              */
5190             "widthchange": true,
5191         /**
5192              * @event headerchange
5193              * Fires when the text of a header changes.
5194              * @param {ColumnModel} this
5195              * @param {Number} columnIndex The column index
5196              * @param {Number} newText The new header text
5197              */
5198             "headerchange": true,
5199         /**
5200              * @event hiddenchange
5201              * Fires when a column is hidden or "unhidden".
5202              * @param {ColumnModel} this
5203              * @param {Number} columnIndex The column index
5204              * @param {Boolean} hidden true if hidden, false otherwise
5205              */
5206             "hiddenchange": true,
5207             /**
5208          * @event columnmoved
5209          * Fires when a column is moved.
5210          * @param {ColumnModel} this
5211          * @param {Number} oldIndex
5212          * @param {Number} newIndex
5213          */
5214         "columnmoved" : true,
5215         /**
5216          * @event columlockchange
5217          * Fires when a column's locked state is changed
5218          * @param {ColumnModel} this
5219          * @param {Number} colIndex
5220          * @param {Boolean} locked true if locked
5221          */
5222         "columnlockchange" : true
5223     });
5224     Roo.grid.ColumnModel.superclass.constructor.call(this);
5225 };
5226 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5227     /**
5228      * @cfg {String} header The header text to display in the Grid view.
5229      */
5230     /**
5231      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5232      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5233      * specified, the column's index is used as an index into the Record's data Array.
5234      */
5235     /**
5236      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5237      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5238      */
5239     /**
5240      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5241      * Defaults to the value of the {@link #defaultSortable} property.
5242      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5243      */
5244     /**
5245      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5246      */
5247     /**
5248      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5249      */
5250     /**
5251      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5252      */
5253     /**
5254      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5255      */
5256     /**
5257      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5258      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5259      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5260      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5261      */
5262        /**
5263      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5264      */
5265     /**
5266      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5267      */
5268     /**
5269      * @cfg {String} cursor (Optional)
5270      */
5271     /**
5272      * @cfg {String} tooltip (Optional)
5273      */
5274     /**
5275      * @cfg {Number} xs (Optional)
5276      */
5277     /**
5278      * @cfg {Number} sm (Optional)
5279      */
5280     /**
5281      * @cfg {Number} md (Optional)
5282      */
5283     /**
5284      * @cfg {Number} lg (Optional)
5285      */
5286     /**
5287      * Returns the id of the column at the specified index.
5288      * @param {Number} index The column index
5289      * @return {String} the id
5290      */
5291     getColumnId : function(index){
5292         return this.config[index].id;
5293     },
5294
5295     /**
5296      * Returns the column for a specified id.
5297      * @param {String} id The column id
5298      * @return {Object} the column
5299      */
5300     getColumnById : function(id){
5301         return this.lookup[id];
5302     },
5303
5304     
5305     /**
5306      * Returns the column for a specified dataIndex.
5307      * @param {String} dataIndex The column dataIndex
5308      * @return {Object|Boolean} the column or false if not found
5309      */
5310     getColumnByDataIndex: function(dataIndex){
5311         var index = this.findColumnIndex(dataIndex);
5312         return index > -1 ? this.config[index] : false;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column id.
5317      * @param {String} id The column id
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     getIndexById : function(id){
5321         for(var i = 0, len = this.config.length; i < len; i++){
5322             if(this.config[i].id == id){
5323                 return i;
5324             }
5325         }
5326         return -1;
5327     },
5328     
5329     /**
5330      * Returns the index for a specified column dataIndex.
5331      * @param {String} dataIndex The column dataIndex
5332      * @return {Number} the index, or -1 if not found
5333      */
5334     
5335     findColumnIndex : function(dataIndex){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(this.config[i].dataIndex == dataIndex){
5338                 return i;
5339             }
5340         }
5341         return -1;
5342     },
5343     
5344     
5345     moveColumn : function(oldIndex, newIndex){
5346         var c = this.config[oldIndex];
5347         this.config.splice(oldIndex, 1);
5348         this.config.splice(newIndex, 0, c);
5349         this.dataMap = null;
5350         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5351     },
5352
5353     isLocked : function(colIndex){
5354         return this.config[colIndex].locked === true;
5355     },
5356
5357     setLocked : function(colIndex, value, suppressEvent){
5358         if(this.isLocked(colIndex) == value){
5359             return;
5360         }
5361         this.config[colIndex].locked = value;
5362         if(!suppressEvent){
5363             this.fireEvent("columnlockchange", this, colIndex, value);
5364         }
5365     },
5366
5367     getTotalLockedWidth : function(){
5368         var totalWidth = 0;
5369         for(var i = 0; i < this.config.length; i++){
5370             if(this.isLocked(i) && !this.isHidden(i)){
5371                 this.totalWidth += this.getColumnWidth(i);
5372             }
5373         }
5374         return totalWidth;
5375     },
5376
5377     getLockedCount : function(){
5378         for(var i = 0, len = this.config.length; i < len; i++){
5379             if(!this.isLocked(i)){
5380                 return i;
5381             }
5382         }
5383         
5384         return this.config.length;
5385     },
5386
5387     /**
5388      * Returns the number of columns.
5389      * @return {Number}
5390      */
5391     getColumnCount : function(visibleOnly){
5392         if(visibleOnly === true){
5393             var c = 0;
5394             for(var i = 0, len = this.config.length; i < len; i++){
5395                 if(!this.isHidden(i)){
5396                     c++;
5397                 }
5398             }
5399             return c;
5400         }
5401         return this.config.length;
5402     },
5403
5404     /**
5405      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5406      * @param {Function} fn
5407      * @param {Object} scope (optional)
5408      * @return {Array} result
5409      */
5410     getColumnsBy : function(fn, scope){
5411         var r = [];
5412         for(var i = 0, len = this.config.length; i < len; i++){
5413             var c = this.config[i];
5414             if(fn.call(scope||this, c, i) === true){
5415                 r[r.length] = c;
5416             }
5417         }
5418         return r;
5419     },
5420
5421     /**
5422      * Returns true if the specified column is sortable.
5423      * @param {Number} col The column index
5424      * @return {Boolean}
5425      */
5426     isSortable : function(col){
5427         if(typeof this.config[col].sortable == "undefined"){
5428             return this.defaultSortable;
5429         }
5430         return this.config[col].sortable;
5431     },
5432
5433     /**
5434      * Returns the rendering (formatting) function defined for the column.
5435      * @param {Number} col The column index.
5436      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5437      */
5438     getRenderer : function(col){
5439         if(!this.config[col].renderer){
5440             return Roo.grid.ColumnModel.defaultRenderer;
5441         }
5442         return this.config[col].renderer;
5443     },
5444
5445     /**
5446      * Sets the rendering (formatting) function for a column.
5447      * @param {Number} col The column index
5448      * @param {Function} fn The function to use to process the cell's raw data
5449      * to return HTML markup for the grid view. The render function is called with
5450      * the following parameters:<ul>
5451      * <li>Data value.</li>
5452      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5453      * <li>css A CSS style string to apply to the table cell.</li>
5454      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5455      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5456      * <li>Row index</li>
5457      * <li>Column index</li>
5458      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5459      */
5460     setRenderer : function(col, fn){
5461         this.config[col].renderer = fn;
5462     },
5463
5464     /**
5465      * Returns the width for the specified column.
5466      * @param {Number} col The column index
5467      * @return {Number}
5468      */
5469     getColumnWidth : function(col){
5470         return this.config[col].width * 1 || this.defaultWidth;
5471     },
5472
5473     /**
5474      * Sets the width for a column.
5475      * @param {Number} col The column index
5476      * @param {Number} width The new width
5477      */
5478     setColumnWidth : function(col, width, suppressEvent){
5479         this.config[col].width = width;
5480         this.totalWidth = null;
5481         if(!suppressEvent){
5482              this.fireEvent("widthchange", this, col, width);
5483         }
5484     },
5485
5486     /**
5487      * Returns the total width of all columns.
5488      * @param {Boolean} includeHidden True to include hidden column widths
5489      * @return {Number}
5490      */
5491     getTotalWidth : function(includeHidden){
5492         if(!this.totalWidth){
5493             this.totalWidth = 0;
5494             for(var i = 0, len = this.config.length; i < len; i++){
5495                 if(includeHidden || !this.isHidden(i)){
5496                     this.totalWidth += this.getColumnWidth(i);
5497                 }
5498             }
5499         }
5500         return this.totalWidth;
5501     },
5502
5503     /**
5504      * Returns the header for the specified column.
5505      * @param {Number} col The column index
5506      * @return {String}
5507      */
5508     getColumnHeader : function(col){
5509         return this.config[col].header;
5510     },
5511
5512     /**
5513      * Sets the header for a column.
5514      * @param {Number} col The column index
5515      * @param {String} header The new header
5516      */
5517     setColumnHeader : function(col, header){
5518         this.config[col].header = header;
5519         this.fireEvent("headerchange", this, col, header);
5520     },
5521
5522     /**
5523      * Returns the tooltip for the specified column.
5524      * @param {Number} col The column index
5525      * @return {String}
5526      */
5527     getColumnTooltip : function(col){
5528             return this.config[col].tooltip;
5529     },
5530     /**
5531      * Sets the tooltip for a column.
5532      * @param {Number} col The column index
5533      * @param {String} tooltip The new tooltip
5534      */
5535     setColumnTooltip : function(col, tooltip){
5536             this.config[col].tooltip = tooltip;
5537     },
5538
5539     /**
5540      * Returns the dataIndex for the specified column.
5541      * @param {Number} col The column index
5542      * @return {Number}
5543      */
5544     getDataIndex : function(col){
5545         return this.config[col].dataIndex;
5546     },
5547
5548     /**
5549      * Sets the dataIndex for a column.
5550      * @param {Number} col The column index
5551      * @param {Number} dataIndex The new dataIndex
5552      */
5553     setDataIndex : function(col, dataIndex){
5554         this.config[col].dataIndex = dataIndex;
5555     },
5556
5557     
5558     
5559     /**
5560      * Returns true if the cell is editable.
5561      * @param {Number} colIndex The column index
5562      * @param {Number} rowIndex The row index - this is nto actually used..?
5563      * @return {Boolean}
5564      */
5565     isCellEditable : function(colIndex, rowIndex){
5566         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5567     },
5568
5569     /**
5570      * Returns the editor defined for the cell/column.
5571      * return false or null to disable editing.
5572      * @param {Number} colIndex The column index
5573      * @param {Number} rowIndex The row index
5574      * @return {Object}
5575      */
5576     getCellEditor : function(colIndex, rowIndex){
5577         return this.config[colIndex].editor;
5578     },
5579
5580     /**
5581      * Sets if a column is editable.
5582      * @param {Number} col The column index
5583      * @param {Boolean} editable True if the column is editable
5584      */
5585     setEditable : function(col, editable){
5586         this.config[col].editable = editable;
5587     },
5588
5589
5590     /**
5591      * Returns true if the column is hidden.
5592      * @param {Number} colIndex The column index
5593      * @return {Boolean}
5594      */
5595     isHidden : function(colIndex){
5596         return this.config[colIndex].hidden;
5597     },
5598
5599
5600     /**
5601      * Returns true if the column width cannot be changed
5602      */
5603     isFixed : function(colIndex){
5604         return this.config[colIndex].fixed;
5605     },
5606
5607     /**
5608      * Returns true if the column can be resized
5609      * @return {Boolean}
5610      */
5611     isResizable : function(colIndex){
5612         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5613     },
5614     /**
5615      * Sets if a column is hidden.
5616      * @param {Number} colIndex The column index
5617      * @param {Boolean} hidden True if the column is hidden
5618      */
5619     setHidden : function(colIndex, hidden){
5620         this.config[colIndex].hidden = hidden;
5621         this.totalWidth = null;
5622         this.fireEvent("hiddenchange", this, colIndex, hidden);
5623     },
5624
5625     /**
5626      * Sets the editor for a column.
5627      * @param {Number} col The column index
5628      * @param {Object} editor The editor object
5629      */
5630     setEditor : function(col, editor){
5631         this.config[col].editor = editor;
5632     }
5633 });
5634
5635 Roo.grid.ColumnModel.defaultRenderer = function(value)
5636 {
5637     if(typeof value == "object") {
5638         return value;
5639     }
5640         if(typeof value == "string" && value.length < 1){
5641             return "&#160;";
5642         }
5643     
5644         return String.format("{0}", value);
5645 };
5646
5647 // Alias for backwards compatibility
5648 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5649 /*
5650  * Based on:
5651  * Ext JS Library 1.1.1
5652  * Copyright(c) 2006-2007, Ext JS, LLC.
5653  *
5654  * Originally Released Under LGPL - original licence link has changed is not relivant.
5655  *
5656  * Fork - LGPL
5657  * <script type="text/javascript">
5658  */
5659  
5660 /**
5661  * @class Roo.LoadMask
5662  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5663  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5664  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5665  * element's UpdateManager load indicator and will be destroyed after the initial load.
5666  * @constructor
5667  * Create a new LoadMask
5668  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5669  * @param {Object} config The config object
5670  */
5671 Roo.LoadMask = function(el, config){
5672     this.el = Roo.get(el);
5673     Roo.apply(this, config);
5674     if(this.store){
5675         this.store.on('beforeload', this.onBeforeLoad, this);
5676         this.store.on('load', this.onLoad, this);
5677         this.store.on('loadexception', this.onLoadException, this);
5678         this.removeMask = false;
5679     }else{
5680         var um = this.el.getUpdateManager();
5681         um.showLoadIndicator = false; // disable the default indicator
5682         um.on('beforeupdate', this.onBeforeLoad, this);
5683         um.on('update', this.onLoad, this);
5684         um.on('failure', this.onLoad, this);
5685         this.removeMask = true;
5686     }
5687 };
5688
5689 Roo.LoadMask.prototype = {
5690     /**
5691      * @cfg {Boolean} removeMask
5692      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5693      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5694      */
5695     /**
5696      * @cfg {String} msg
5697      * The text to display in a centered loading message box (defaults to 'Loading...')
5698      */
5699     msg : 'Loading...',
5700     /**
5701      * @cfg {String} msgCls
5702      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5703      */
5704     msgCls : 'x-mask-loading',
5705
5706     /**
5707      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5708      * @type Boolean
5709      */
5710     disabled: false,
5711
5712     /**
5713      * Disables the mask to prevent it from being displayed
5714      */
5715     disable : function(){
5716        this.disabled = true;
5717     },
5718
5719     /**
5720      * Enables the mask so that it can be displayed
5721      */
5722     enable : function(){
5723         this.disabled = false;
5724     },
5725     
5726     onLoadException : function()
5727     {
5728         Roo.log(arguments);
5729         
5730         if (typeof(arguments[3]) != 'undefined') {
5731             Roo.MessageBox.alert("Error loading",arguments[3]);
5732         } 
5733         /*
5734         try {
5735             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5736                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5737             }   
5738         } catch(e) {
5739             
5740         }
5741         */
5742     
5743         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5744     },
5745     // private
5746     onLoad : function()
5747     {
5748         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5749     },
5750
5751     // private
5752     onBeforeLoad : function(){
5753         if(!this.disabled){
5754             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5755         }
5756     },
5757
5758     // private
5759     destroy : function(){
5760         if(this.store){
5761             this.store.un('beforeload', this.onBeforeLoad, this);
5762             this.store.un('load', this.onLoad, this);
5763             this.store.un('loadexception', this.onLoadException, this);
5764         }else{
5765             var um = this.el.getUpdateManager();
5766             um.un('beforeupdate', this.onBeforeLoad, this);
5767             um.un('update', this.onLoad, this);
5768             um.un('failure', this.onLoad, this);
5769         }
5770     }
5771 };/*
5772  * - LGPL
5773  *
5774  * table
5775  * 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.Table
5780  * @extends Roo.bootstrap.Component
5781  * Bootstrap Table class
5782  * @cfg {String} cls table class
5783  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5784  * @cfg {String} bgcolor Specifies the background color for a table
5785  * @cfg {Number} border Specifies whether the table cells should have borders or not
5786  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5787  * @cfg {Number} cellspacing Specifies the space between cells
5788  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5789  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5790  * @cfg {String} sortable Specifies that the table should be sortable
5791  * @cfg {String} summary Specifies a summary of the content of a table
5792  * @cfg {Number} width Specifies the width of a table
5793  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5794  * 
5795  * @cfg {boolean} striped Should the rows be alternative striped
5796  * @cfg {boolean} bordered Add borders to the table
5797  * @cfg {boolean} hover Add hover highlighting
5798  * @cfg {boolean} condensed Format condensed
5799  * @cfg {boolean} responsive Format condensed
5800  * @cfg {Boolean} loadMask (true|false) default false
5801  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5802  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5803  * @cfg {Boolean} rowSelection (true|false) default false
5804  * @cfg {Boolean} cellSelection (true|false) default false
5805  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5806  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5807  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5808  
5809  * 
5810  * @constructor
5811  * Create a new Table
5812  * @param {Object} config The config object
5813  */
5814
5815 Roo.bootstrap.Table = function(config){
5816     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5817     
5818   
5819     
5820     // BC...
5821     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5822     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5823     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5824     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5825     
5826     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5827     if (this.sm) {
5828         this.sm.grid = this;
5829         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5830         this.sm = this.selModel;
5831         this.sm.xmodule = this.xmodule || false;
5832     }
5833     
5834     if (this.cm && typeof(this.cm.config) == 'undefined') {
5835         this.colModel = new Roo.grid.ColumnModel(this.cm);
5836         this.cm = this.colModel;
5837         this.cm.xmodule = this.xmodule || false;
5838     }
5839     if (this.store) {
5840         this.store= Roo.factory(this.store, Roo.data);
5841         this.ds = this.store;
5842         this.ds.xmodule = this.xmodule || false;
5843          
5844     }
5845     if (this.footer && this.store) {
5846         this.footer.dataSource = this.ds;
5847         this.footer = Roo.factory(this.footer);
5848     }
5849     
5850     /** @private */
5851     this.addEvents({
5852         /**
5853          * @event cellclick
5854          * Fires when a cell is clicked
5855          * @param {Roo.bootstrap.Table} this
5856          * @param {Roo.Element} el
5857          * @param {Number} rowIndex
5858          * @param {Number} columnIndex
5859          * @param {Roo.EventObject} e
5860          */
5861         "cellclick" : true,
5862         /**
5863          * @event celldblclick
5864          * Fires when a cell is double clicked
5865          * @param {Roo.bootstrap.Table} this
5866          * @param {Roo.Element} el
5867          * @param {Number} rowIndex
5868          * @param {Number} columnIndex
5869          * @param {Roo.EventObject} e
5870          */
5871         "celldblclick" : true,
5872         /**
5873          * @event rowclick
5874          * Fires when a row is clicked
5875          * @param {Roo.bootstrap.Table} this
5876          * @param {Roo.Element} el
5877          * @param {Number} rowIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "rowclick" : true,
5881         /**
5882          * @event rowdblclick
5883          * Fires when a row is double clicked
5884          * @param {Roo.bootstrap.Table} this
5885          * @param {Roo.Element} el
5886          * @param {Number} rowIndex
5887          * @param {Roo.EventObject} e
5888          */
5889         "rowdblclick" : true,
5890         /**
5891          * @event mouseover
5892          * Fires when a mouseover occur
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Roo.Element} el
5895          * @param {Number} rowIndex
5896          * @param {Number} columnIndex
5897          * @param {Roo.EventObject} e
5898          */
5899         "mouseover" : true,
5900         /**
5901          * @event mouseout
5902          * Fires when a mouseout occur
5903          * @param {Roo.bootstrap.Table} this
5904          * @param {Roo.Element} el
5905          * @param {Number} rowIndex
5906          * @param {Number} columnIndex
5907          * @param {Roo.EventObject} e
5908          */
5909         "mouseout" : true,
5910         /**
5911          * @event rowclass
5912          * Fires when a row is rendered, so you can change add a style to it.
5913          * @param {Roo.bootstrap.Table} this
5914          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5915          */
5916         'rowclass' : true,
5917           /**
5918          * @event rowsrendered
5919          * Fires when all the  rows have been rendered
5920          * @param {Roo.bootstrap.Table} this
5921          */
5922         'rowsrendered' : true,
5923         /**
5924          * @event contextmenu
5925          * The raw contextmenu event for the entire grid.
5926          * @param {Roo.EventObject} e
5927          */
5928         "contextmenu" : true,
5929         /**
5930          * @event rowcontextmenu
5931          * Fires when a row is right clicked
5932          * @param {Roo.bootstrap.Table} this
5933          * @param {Number} rowIndex
5934          * @param {Roo.EventObject} e
5935          */
5936         "rowcontextmenu" : true,
5937         /**
5938          * @event cellcontextmenu
5939          * Fires when a cell is right clicked
5940          * @param {Roo.bootstrap.Table} this
5941          * @param {Number} rowIndex
5942          * @param {Number} cellIndex
5943          * @param {Roo.EventObject} e
5944          */
5945          "cellcontextmenu" : true,
5946          /**
5947          * @event headercontextmenu
5948          * Fires when a header is right clicked
5949          * @param {Roo.bootstrap.Table} this
5950          * @param {Number} columnIndex
5951          * @param {Roo.EventObject} e
5952          */
5953         "headercontextmenu" : true
5954     });
5955 };
5956
5957 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5958     
5959     cls: false,
5960     align: false,
5961     bgcolor: false,
5962     border: false,
5963     cellpadding: false,
5964     cellspacing: false,
5965     frame: false,
5966     rules: false,
5967     sortable: false,
5968     summary: false,
5969     width: false,
5970     striped : false,
5971     scrollBody : false,
5972     bordered: false,
5973     hover:  false,
5974     condensed : false,
5975     responsive : false,
5976     sm : false,
5977     cm : false,
5978     store : false,
5979     loadMask : false,
5980     footerShow : true,
5981     headerShow : true,
5982   
5983     rowSelection : false,
5984     cellSelection : false,
5985     layout : false,
5986     
5987     // Roo.Element - the tbody
5988     mainBody: false,
5989     // Roo.Element - thead element
5990     mainHead: false,
5991     
5992     container: false, // used by gridpanel...
5993     
5994     lazyLoad : false,
5995     
5996     getAutoCreate : function()
5997     {
5998         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5999         
6000         cfg = {
6001             tag: 'table',
6002             cls : 'table',
6003             cn : []
6004         };
6005         if (this.scrollBody) {
6006             cfg.cls += ' table-body-fixed';
6007         }    
6008         if (this.striped) {
6009             cfg.cls += ' table-striped';
6010         }
6011         
6012         if (this.hover) {
6013             cfg.cls += ' table-hover';
6014         }
6015         if (this.bordered) {
6016             cfg.cls += ' table-bordered';
6017         }
6018         if (this.condensed) {
6019             cfg.cls += ' table-condensed';
6020         }
6021         if (this.responsive) {
6022             cfg.cls += ' table-responsive';
6023         }
6024         
6025         if (this.cls) {
6026             cfg.cls+=  ' ' +this.cls;
6027         }
6028         
6029         // this lot should be simplifed...
6030         
6031         if (this.align) {
6032             cfg.align=this.align;
6033         }
6034         if (this.bgcolor) {
6035             cfg.bgcolor=this.bgcolor;
6036         }
6037         if (this.border) {
6038             cfg.border=this.border;
6039         }
6040         if (this.cellpadding) {
6041             cfg.cellpadding=this.cellpadding;
6042         }
6043         if (this.cellspacing) {
6044             cfg.cellspacing=this.cellspacing;
6045         }
6046         if (this.frame) {
6047             cfg.frame=this.frame;
6048         }
6049         if (this.rules) {
6050             cfg.rules=this.rules;
6051         }
6052         if (this.sortable) {
6053             cfg.sortable=this.sortable;
6054         }
6055         if (this.summary) {
6056             cfg.summary=this.summary;
6057         }
6058         if (this.width) {
6059             cfg.width=this.width;
6060         }
6061         if (this.layout) {
6062             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6063         }
6064         
6065         if(this.store || this.cm){
6066             if(this.headerShow){
6067                 cfg.cn.push(this.renderHeader());
6068             }
6069             
6070             cfg.cn.push(this.renderBody());
6071             
6072             if(this.footerShow){
6073                 cfg.cn.push(this.renderFooter());
6074             }
6075             // where does this come from?
6076             //cfg.cls+=  ' TableGrid';
6077         }
6078         
6079         return { cn : [ cfg ] };
6080     },
6081     
6082     initEvents : function()
6083     {   
6084         if(!this.store || !this.cm){
6085             return;
6086         }
6087         if (this.selModel) {
6088             this.selModel.initEvents();
6089         }
6090         
6091         
6092         //Roo.log('initEvents with ds!!!!');
6093         
6094         this.mainBody = this.el.select('tbody', true).first();
6095         this.mainHead = this.el.select('thead', true).first();
6096         
6097         
6098         
6099         
6100         var _this = this;
6101         
6102         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6103             e.on('click', _this.sort, _this);
6104         });
6105         
6106         this.mainBody.on("click", this.onClick, this);
6107         this.mainBody.on("dblclick", this.onDblClick, this);
6108         
6109         // why is this done????? = it breaks dialogs??
6110         //this.parent().el.setStyle('position', 'relative');
6111         
6112         
6113         if (this.footer) {
6114             this.footer.parentId = this.id;
6115             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6116             
6117             if(this.lazyLoad){
6118                 this.el.select('tfoot tr td').first().addClass('hide');
6119             }
6120         } 
6121         
6122         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6123         
6124         this.store.on('load', this.onLoad, this);
6125         this.store.on('beforeload', this.onBeforeLoad, this);
6126         this.store.on('update', this.onUpdate, this);
6127         this.store.on('add', this.onAdd, this);
6128         this.store.on("clear", this.clear, this);
6129         
6130         this.el.on("contextmenu", this.onContextMenu, this);
6131         
6132         this.mainBody.on('scroll', this.onBodyScroll, this);
6133         
6134         this.cm.on("headerchange", this.onHeaderChange, this);
6135         
6136     },
6137     
6138     onContextMenu : function(e, t)
6139     {
6140         this.processEvent("contextmenu", e);
6141     },
6142     
6143     processEvent : function(name, e)
6144     {
6145         if (name != 'touchstart' ) {
6146             this.fireEvent(name, e);    
6147         }
6148         
6149         var t = e.getTarget();
6150         
6151         var cell = Roo.get(t);
6152         
6153         if(!cell){
6154             return;
6155         }
6156         
6157         if(cell.findParent('tfoot', false, true)){
6158             return;
6159         }
6160         
6161         if(cell.findParent('thead', false, true)){
6162             
6163             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6164                 cell = Roo.get(t).findParent('th', false, true);
6165                 if (!cell) {
6166                     Roo.log("failed to find th in thead?");
6167                     Roo.log(e.getTarget());
6168                     return;
6169                 }
6170             }
6171             
6172             var cellIndex = cell.dom.cellIndex;
6173             
6174             var ename = name == 'touchstart' ? 'click' : name;
6175             this.fireEvent("header" + ename, this, cellIndex, e);
6176             
6177             return;
6178         }
6179         
6180         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6181             cell = Roo.get(t).findParent('td', false, true);
6182             if (!cell) {
6183                 Roo.log("failed to find th in tbody?");
6184                 Roo.log(e.getTarget());
6185                 return;
6186             }
6187         }
6188         
6189         var row = cell.findParent('tr', false, true);
6190         var cellIndex = cell.dom.cellIndex;
6191         var rowIndex = row.dom.rowIndex - 1;
6192         
6193         if(row !== false){
6194             
6195             this.fireEvent("row" + name, this, rowIndex, e);
6196             
6197             if(cell !== false){
6198             
6199                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6200             }
6201         }
6202         
6203     },
6204     
6205     onMouseover : function(e, el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         var row = cell.findParent('tr', false, true);
6218         var cellIndex = cell.dom.cellIndex;
6219         var rowIndex = row.dom.rowIndex - 1; // start from 0
6220         
6221         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6222         
6223     },
6224     
6225     onMouseout : function(e, el)
6226     {
6227         var cell = Roo.get(el);
6228         
6229         if(!cell){
6230             return;
6231         }
6232         
6233         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6234             cell = cell.findParent('td', false, true);
6235         }
6236         
6237         var row = cell.findParent('tr', false, true);
6238         var cellIndex = cell.dom.cellIndex;
6239         var rowIndex = row.dom.rowIndex - 1; // start from 0
6240         
6241         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6242         
6243     },
6244     
6245     onClick : function(e, el)
6246     {
6247         var cell = Roo.get(el);
6248         
6249         if(!cell || (!this.cellSelection && !this.rowSelection)){
6250             return;
6251         }
6252         
6253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254             cell = cell.findParent('td', false, true);
6255         }
6256         
6257         if(!cell || typeof(cell) == 'undefined'){
6258             return;
6259         }
6260         
6261         var row = cell.findParent('tr', false, true);
6262         
6263         if(!row || typeof(row) == 'undefined'){
6264             return;
6265         }
6266         
6267         var cellIndex = cell.dom.cellIndex;
6268         var rowIndex = this.getRowIndex(row);
6269         
6270         // why??? - should these not be based on SelectionModel?
6271         if(this.cellSelection){
6272             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6273         }
6274         
6275         if(this.rowSelection){
6276             this.fireEvent('rowclick', this, row, rowIndex, e);
6277         }
6278         
6279         
6280     },
6281         
6282     onDblClick : function(e,el)
6283     {
6284         var cell = Roo.get(el);
6285         
6286         if(!cell || (!this.cellSelection && !this.rowSelection)){
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = cell.findParent('td', false, true);
6292         }
6293         
6294         if(!cell || typeof(cell) == 'undefined'){
6295             return;
6296         }
6297         
6298         var row = cell.findParent('tr', false, true);
6299         
6300         if(!row || typeof(row) == 'undefined'){
6301             return;
6302         }
6303         
6304         var cellIndex = cell.dom.cellIndex;
6305         var rowIndex = this.getRowIndex(row);
6306         
6307         if(this.cellSelection){
6308             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6309         }
6310         
6311         if(this.rowSelection){
6312             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6313         }
6314     },
6315     
6316     sort : function(e,el)
6317     {
6318         var col = Roo.get(el);
6319         
6320         if(!col.hasClass('sortable')){
6321             return;
6322         }
6323         
6324         var sort = col.attr('sort');
6325         var dir = 'ASC';
6326         
6327         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6328             dir = 'DESC';
6329         }
6330         
6331         this.store.sortInfo = {field : sort, direction : dir};
6332         
6333         if (this.footer) {
6334             Roo.log("calling footer first");
6335             this.footer.onClick('first');
6336         } else {
6337         
6338             this.store.load({ params : { start : 0 } });
6339         }
6340     },
6341     
6342     renderHeader : function()
6343     {
6344         var header = {
6345             tag: 'thead',
6346             cn : []
6347         };
6348         
6349         var cm = this.cm;
6350         this.totalWidth = 0;
6351         
6352         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6353             
6354             var config = cm.config[i];
6355             
6356             var c = {
6357                 tag: 'th',
6358                 style : '',
6359                 html: cm.getColumnHeader(i)
6360             };
6361             
6362             var hh = '';
6363             
6364             if(typeof(config.sortable) != 'undefined' && config.sortable){
6365                 c.cls = 'sortable';
6366                 c.html = '<i class="glyphicon"></i>' + c.html;
6367             }
6368             
6369             if(typeof(config.lgHeader) != 'undefined'){
6370                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6371             }
6372             
6373             if(typeof(config.mdHeader) != 'undefined'){
6374                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6375             }
6376             
6377             if(typeof(config.smHeader) != 'undefined'){
6378                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6379             }
6380             
6381             if(typeof(config.xsHeader) != 'undefined'){
6382                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6383             }
6384             
6385             if(hh.length){
6386                 c.html = hh;
6387             }
6388             
6389             if(typeof(config.tooltip) != 'undefined'){
6390                 c.tooltip = config.tooltip;
6391             }
6392             
6393             if(typeof(config.colspan) != 'undefined'){
6394                 c.colspan = config.colspan;
6395             }
6396             
6397             if(typeof(config.hidden) != 'undefined' && config.hidden){
6398                 c.style += ' display:none;';
6399             }
6400             
6401             if(typeof(config.dataIndex) != 'undefined'){
6402                 c.sort = config.dataIndex;
6403             }
6404             
6405            
6406             
6407             if(typeof(config.align) != 'undefined' && config.align.length){
6408                 c.style += ' text-align:' + config.align + ';';
6409             }
6410             
6411             if(typeof(config.width) != 'undefined'){
6412                 c.style += ' width:' + config.width + 'px;';
6413                 this.totalWidth += config.width;
6414             } else {
6415                 this.totalWidth += 100; // assume minimum of 100 per column?
6416             }
6417             
6418             if(typeof(config.cls) != 'undefined'){
6419                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6420             }
6421             
6422             ['xs','sm','md','lg'].map(function(size){
6423                 
6424                 if(typeof(config[size]) == 'undefined'){
6425                     return;
6426                 }
6427                 
6428                 if (!config[size]) { // 0 = hidden
6429                     c.cls += ' hidden-' + size;
6430                     return;
6431                 }
6432                 
6433                 c.cls += ' col-' + size + '-' + config[size];
6434
6435             });
6436             
6437             header.cn.push(c)
6438         }
6439         
6440         return header;
6441     },
6442     
6443     renderBody : function()
6444     {
6445         var body = {
6446             tag: 'tbody',
6447             cn : [
6448                 {
6449                     tag: 'tr',
6450                     cn : [
6451                         {
6452                             tag : 'td',
6453                             colspan :  this.cm.getColumnCount()
6454                         }
6455                     ]
6456                 }
6457             ]
6458         };
6459         
6460         return body;
6461     },
6462     
6463     renderFooter : function()
6464     {
6465         var footer = {
6466             tag: 'tfoot',
6467             cn : [
6468                 {
6469                     tag: 'tr',
6470                     cn : [
6471                         {
6472                             tag : 'td',
6473                             colspan :  this.cm.getColumnCount()
6474                         }
6475                     ]
6476                 }
6477             ]
6478         };
6479         
6480         return footer;
6481     },
6482     
6483     
6484     
6485     onLoad : function()
6486     {
6487 //        Roo.log('ds onload');
6488         this.clear();
6489         
6490         var _this = this;
6491         var cm = this.cm;
6492         var ds = this.store;
6493         
6494         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6495             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6496             if (_this.store.sortInfo) {
6497                     
6498                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6499                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6500                 }
6501                 
6502                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6503                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6504                 }
6505             }
6506         });
6507         
6508         var tbody =  this.mainBody;
6509               
6510         if(ds.getCount() > 0){
6511             ds.data.each(function(d,rowIndex){
6512                 var row =  this.renderRow(cm, ds, rowIndex);
6513                 
6514                 tbody.createChild(row);
6515                 
6516                 var _this = this;
6517                 
6518                 if(row.cellObjects.length){
6519                     Roo.each(row.cellObjects, function(r){
6520                         _this.renderCellObject(r);
6521                     })
6522                 }
6523                 
6524             }, this);
6525         }
6526         
6527         Roo.each(this.el.select('tbody td', true).elements, function(e){
6528             e.on('mouseover', _this.onMouseover, _this);
6529         });
6530         
6531         Roo.each(this.el.select('tbody td', true).elements, function(e){
6532             e.on('mouseout', _this.onMouseout, _this);
6533         });
6534         this.fireEvent('rowsrendered', this);
6535         //if(this.loadMask){
6536         //    this.maskEl.hide();
6537         //}
6538         
6539         this.autoSize();
6540     },
6541     
6542     
6543     onUpdate : function(ds,record)
6544     {
6545         this.refreshRow(record);
6546         this.autoSize();
6547     },
6548     
6549     onRemove : function(ds, record, index, isUpdate){
6550         if(isUpdate !== true){
6551             this.fireEvent("beforerowremoved", this, index, record);
6552         }
6553         var bt = this.mainBody.dom;
6554         
6555         var rows = this.el.select('tbody > tr', true).elements;
6556         
6557         if(typeof(rows[index]) != 'undefined'){
6558             bt.removeChild(rows[index].dom);
6559         }
6560         
6561 //        if(bt.rows[index]){
6562 //            bt.removeChild(bt.rows[index]);
6563 //        }
6564         
6565         if(isUpdate !== true){
6566             //this.stripeRows(index);
6567             //this.syncRowHeights(index, index);
6568             //this.layout();
6569             this.fireEvent("rowremoved", this, index, record);
6570         }
6571     },
6572     
6573     onAdd : function(ds, records, rowIndex)
6574     {
6575         //Roo.log('on Add called');
6576         // - note this does not handle multiple adding very well..
6577         var bt = this.mainBody.dom;
6578         for (var i =0 ; i < records.length;i++) {
6579             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6580             //Roo.log(records[i]);
6581             //Roo.log(this.store.getAt(rowIndex+i));
6582             this.insertRow(this.store, rowIndex + i, false);
6583             return;
6584         }
6585         
6586     },
6587     
6588     
6589     refreshRow : function(record){
6590         var ds = this.store, index;
6591         if(typeof record == 'number'){
6592             index = record;
6593             record = ds.getAt(index);
6594         }else{
6595             index = ds.indexOf(record);
6596         }
6597         this.insertRow(ds, index, true);
6598         this.autoSize();
6599         this.onRemove(ds, record, index+1, true);
6600         this.autoSize();
6601         //this.syncRowHeights(index, index);
6602         //this.layout();
6603         this.fireEvent("rowupdated", this, index, record);
6604     },
6605     
6606     insertRow : function(dm, rowIndex, isUpdate){
6607         
6608         if(!isUpdate){
6609             this.fireEvent("beforerowsinserted", this, rowIndex);
6610         }
6611             //var s = this.getScrollState();
6612         var row = this.renderRow(this.cm, this.store, rowIndex);
6613         // insert before rowIndex..
6614         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6615         
6616         var _this = this;
6617                 
6618         if(row.cellObjects.length){
6619             Roo.each(row.cellObjects, function(r){
6620                 _this.renderCellObject(r);
6621             })
6622         }
6623             
6624         if(!isUpdate){
6625             this.fireEvent("rowsinserted", this, rowIndex);
6626             //this.syncRowHeights(firstRow, lastRow);
6627             //this.stripeRows(firstRow);
6628             //this.layout();
6629         }
6630         
6631     },
6632     
6633     
6634     getRowDom : function(rowIndex)
6635     {
6636         var rows = this.el.select('tbody > tr', true).elements;
6637         
6638         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6639         
6640     },
6641     // returns the object tree for a tr..
6642   
6643     
6644     renderRow : function(cm, ds, rowIndex) 
6645     {
6646         
6647         var d = ds.getAt(rowIndex);
6648         
6649         var row = {
6650             tag : 'tr',
6651             cn : []
6652         };
6653             
6654         var cellObjects = [];
6655         
6656         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6657             var config = cm.config[i];
6658             
6659             var renderer = cm.getRenderer(i);
6660             var value = '';
6661             var id = false;
6662             
6663             if(typeof(renderer) !== 'undefined'){
6664                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6665             }
6666             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6667             // and are rendered into the cells after the row is rendered - using the id for the element.
6668             
6669             if(typeof(value) === 'object'){
6670                 id = Roo.id();
6671                 cellObjects.push({
6672                     container : id,
6673                     cfg : value 
6674                 })
6675             }
6676             
6677             var rowcfg = {
6678                 record: d,
6679                 rowIndex : rowIndex,
6680                 colIndex : i,
6681                 rowClass : ''
6682             };
6683
6684             this.fireEvent('rowclass', this, rowcfg);
6685             
6686             var td = {
6687                 tag: 'td',
6688                 cls : rowcfg.rowClass,
6689                 style: '',
6690                 html: (typeof(value) === 'object') ? '' : value
6691             };
6692             
6693             if (id) {
6694                 td.id = id;
6695             }
6696             
6697             if(typeof(config.colspan) != 'undefined'){
6698                 td.colspan = config.colspan;
6699             }
6700             
6701             if(typeof(config.hidden) != 'undefined' && config.hidden){
6702                 td.style += ' display:none;';
6703             }
6704             
6705             if(typeof(config.align) != 'undefined' && config.align.length){
6706                 td.style += ' text-align:' + config.align + ';';
6707             }
6708             
6709             if(typeof(config.width) != 'undefined'){
6710                 td.style += ' width:' +  config.width + 'px;';
6711             }
6712             
6713             if(typeof(config.cursor) != 'undefined'){
6714                 td.style += ' cursor:' +  config.cursor + ';';
6715             }
6716             
6717             if(typeof(config.cls) != 'undefined'){
6718                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6719             }
6720             
6721             ['xs','sm','md','lg'].map(function(size){
6722                 
6723                 if(typeof(config[size]) == 'undefined'){
6724                     return;
6725                 }
6726                 
6727                 if (!config[size]) { // 0 = hidden
6728                     td.cls += ' hidden-' + size;
6729                     return;
6730                 }
6731                 
6732                 td.cls += ' col-' + size + '-' + config[size];
6733
6734             });
6735              
6736             row.cn.push(td);
6737            
6738         }
6739         
6740         row.cellObjects = cellObjects;
6741         
6742         return row;
6743           
6744     },
6745     
6746     
6747     
6748     onBeforeLoad : function()
6749     {
6750         //Roo.log('ds onBeforeLoad');
6751         
6752         //this.clear();
6753         
6754         //if(this.loadMask){
6755         //    this.maskEl.show();
6756         //}
6757     },
6758      /**
6759      * Remove all rows
6760      */
6761     clear : function()
6762     {
6763         this.el.select('tbody', true).first().dom.innerHTML = '';
6764     },
6765     /**
6766      * Show or hide a row.
6767      * @param {Number} rowIndex to show or hide
6768      * @param {Boolean} state hide
6769      */
6770     setRowVisibility : function(rowIndex, state)
6771     {
6772         var bt = this.mainBody.dom;
6773         
6774         var rows = this.el.select('tbody > tr', true).elements;
6775         
6776         if(typeof(rows[rowIndex]) == 'undefined'){
6777             return;
6778         }
6779         rows[rowIndex].dom.style.display = state ? '' : 'none';
6780     },
6781     
6782     
6783     getSelectionModel : function(){
6784         if(!this.selModel){
6785             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6786         }
6787         return this.selModel;
6788     },
6789     /*
6790      * Render the Roo.bootstrap object from renderder
6791      */
6792     renderCellObject : function(r)
6793     {
6794         var _this = this;
6795         
6796         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6797         
6798         var t = r.cfg.render(r.container);
6799         
6800         if(r.cfg.cn){
6801             Roo.each(r.cfg.cn, function(c){
6802                 var child = {
6803                     container: t.getChildContainer(),
6804                     cfg: c
6805                 };
6806                 _this.renderCellObject(child);
6807             })
6808         }
6809     },
6810     
6811     getRowIndex : function(row)
6812     {
6813         var rowIndex = -1;
6814         
6815         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6816             if(el != row){
6817                 return;
6818             }
6819             
6820             rowIndex = index;
6821         });
6822         
6823         return rowIndex;
6824     },
6825      /**
6826      * Returns the grid's underlying element = used by panel.Grid
6827      * @return {Element} The element
6828      */
6829     getGridEl : function(){
6830         return this.el;
6831     },
6832      /**
6833      * Forces a resize - used by panel.Grid
6834      * @return {Element} The element
6835      */
6836     autoSize : function()
6837     {
6838         //var ctr = Roo.get(this.container.dom.parentElement);
6839         var ctr = Roo.get(this.el.dom);
6840         
6841         var thd = this.getGridEl().select('thead',true).first();
6842         var tbd = this.getGridEl().select('tbody', true).first();
6843         var tfd = this.getGridEl().select('tfoot', true).first();
6844         
6845         var cw = ctr.getWidth();
6846         
6847         if (tbd) {
6848             
6849             tbd.setSize(ctr.getWidth(),
6850                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6851             );
6852             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6853             cw -= barsize;
6854         }
6855         cw = Math.max(cw, this.totalWidth);
6856         this.getGridEl().select('tr',true).setWidth(cw);
6857         // resize 'expandable coloumn?
6858         
6859         return; // we doe not have a view in this design..
6860         
6861     },
6862     onBodyScroll: function()
6863     {
6864         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6865         if(this.mainHead){
6866             this.mainHead.setStyle({
6867                 'position' : 'relative',
6868                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6869             });
6870         }
6871         
6872         if(this.lazyLoad){
6873             
6874             var scrollHeight = this.mainBody.dom.scrollHeight;
6875             
6876             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6877             
6878             var height = this.mainBody.getHeight();
6879             
6880             if(scrollHeight - height == scrollTop) {
6881                 
6882                 var total = this.ds.getTotalCount();
6883                 
6884                 if(this.footer.cursor + this.footer.pageSize < total){
6885                     
6886                     this.footer.ds.load({
6887                         params : {
6888                             start : this.footer.cursor + this.footer.pageSize,
6889                             limit : this.footer.pageSize
6890                         },
6891                         add : true
6892                     });
6893                 }
6894             }
6895             
6896         }
6897     },
6898     
6899     onHeaderChange : function()
6900     {
6901         
6902         var header = this.renderHeader();
6903         var table = this.el.select('table', true).first();
6904         
6905         this.mainHead.remove();
6906         this.mainHead = table.createChild(header, this.mainBody, false);
6907     }
6908     
6909 });
6910
6911  
6912
6913  /*
6914  * - LGPL
6915  *
6916  * table cell
6917  * 
6918  */
6919
6920 /**
6921  * @class Roo.bootstrap.TableCell
6922  * @extends Roo.bootstrap.Component
6923  * Bootstrap TableCell class
6924  * @cfg {String} html cell contain text
6925  * @cfg {String} cls cell class
6926  * @cfg {String} tag cell tag (td|th) default td
6927  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6928  * @cfg {String} align Aligns the content in a cell
6929  * @cfg {String} axis Categorizes cells
6930  * @cfg {String} bgcolor Specifies the background color of a cell
6931  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6932  * @cfg {Number} colspan Specifies the number of columns a cell should span
6933  * @cfg {String} headers Specifies one or more header cells a cell is related to
6934  * @cfg {Number} height Sets the height of a cell
6935  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6936  * @cfg {Number} rowspan Sets the number of rows a cell should span
6937  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6938  * @cfg {String} valign Vertical aligns the content in a cell
6939  * @cfg {Number} width Specifies the width of a cell
6940  * 
6941  * @constructor
6942  * Create a new TableCell
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.TableCell = function(config){
6947     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6948 };
6949
6950 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6951     
6952     html: false,
6953     cls: false,
6954     tag: false,
6955     abbr: false,
6956     align: false,
6957     axis: false,
6958     bgcolor: false,
6959     charoff: false,
6960     colspan: false,
6961     headers: false,
6962     height: false,
6963     nowrap: false,
6964     rowspan: false,
6965     scope: false,
6966     valign: false,
6967     width: false,
6968     
6969     
6970     getAutoCreate : function(){
6971         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6972         
6973         cfg = {
6974             tag: 'td'
6975         };
6976         
6977         if(this.tag){
6978             cfg.tag = this.tag;
6979         }
6980         
6981         if (this.html) {
6982             cfg.html=this.html
6983         }
6984         if (this.cls) {
6985             cfg.cls=this.cls
6986         }
6987         if (this.abbr) {
6988             cfg.abbr=this.abbr
6989         }
6990         if (this.align) {
6991             cfg.align=this.align
6992         }
6993         if (this.axis) {
6994             cfg.axis=this.axis
6995         }
6996         if (this.bgcolor) {
6997             cfg.bgcolor=this.bgcolor
6998         }
6999         if (this.charoff) {
7000             cfg.charoff=this.charoff
7001         }
7002         if (this.colspan) {
7003             cfg.colspan=this.colspan
7004         }
7005         if (this.headers) {
7006             cfg.headers=this.headers
7007         }
7008         if (this.height) {
7009             cfg.height=this.height
7010         }
7011         if (this.nowrap) {
7012             cfg.nowrap=this.nowrap
7013         }
7014         if (this.rowspan) {
7015             cfg.rowspan=this.rowspan
7016         }
7017         if (this.scope) {
7018             cfg.scope=this.scope
7019         }
7020         if (this.valign) {
7021             cfg.valign=this.valign
7022         }
7023         if (this.width) {
7024             cfg.width=this.width
7025         }
7026         
7027         
7028         return cfg;
7029     }
7030    
7031 });
7032
7033  
7034
7035  /*
7036  * - LGPL
7037  *
7038  * table row
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.TableRow
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap TableRow class
7046  * @cfg {String} cls row class
7047  * @cfg {String} align Aligns the content in a table row
7048  * @cfg {String} bgcolor Specifies a background color for a table row
7049  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7050  * @cfg {String} valign Vertical aligns the content in a table row
7051  * 
7052  * @constructor
7053  * Create a new TableRow
7054  * @param {Object} config The config object
7055  */
7056
7057 Roo.bootstrap.TableRow = function(config){
7058     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7059 };
7060
7061 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7062     
7063     cls: false,
7064     align: false,
7065     bgcolor: false,
7066     charoff: false,
7067     valign: false,
7068     
7069     getAutoCreate : function(){
7070         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7071         
7072         cfg = {
7073             tag: 'tr'
7074         };
7075             
7076         if(this.cls){
7077             cfg.cls = this.cls;
7078         }
7079         if(this.align){
7080             cfg.align = this.align;
7081         }
7082         if(this.bgcolor){
7083             cfg.bgcolor = this.bgcolor;
7084         }
7085         if(this.charoff){
7086             cfg.charoff = this.charoff;
7087         }
7088         if(this.valign){
7089             cfg.valign = this.valign;
7090         }
7091         
7092         return cfg;
7093     }
7094    
7095 });
7096
7097  
7098
7099  /*
7100  * - LGPL
7101  *
7102  * table body
7103  * 
7104  */
7105
7106 /**
7107  * @class Roo.bootstrap.TableBody
7108  * @extends Roo.bootstrap.Component
7109  * Bootstrap TableBody class
7110  * @cfg {String} cls element class
7111  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7112  * @cfg {String} align Aligns the content inside the element
7113  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7114  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7115  * 
7116  * @constructor
7117  * Create a new TableBody
7118  * @param {Object} config The config object
7119  */
7120
7121 Roo.bootstrap.TableBody = function(config){
7122     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7123 };
7124
7125 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7126     
7127     cls: false,
7128     tag: false,
7129     align: false,
7130     charoff: false,
7131     valign: false,
7132     
7133     getAutoCreate : function(){
7134         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7135         
7136         cfg = {
7137             tag: 'tbody'
7138         };
7139             
7140         if (this.cls) {
7141             cfg.cls=this.cls
7142         }
7143         if(this.tag){
7144             cfg.tag = this.tag;
7145         }
7146         
7147         if(this.align){
7148             cfg.align = this.align;
7149         }
7150         if(this.charoff){
7151             cfg.charoff = this.charoff;
7152         }
7153         if(this.valign){
7154             cfg.valign = this.valign;
7155         }
7156         
7157         return cfg;
7158     }
7159     
7160     
7161 //    initEvents : function()
7162 //    {
7163 //        
7164 //        if(!this.store){
7165 //            return;
7166 //        }
7167 //        
7168 //        this.store = Roo.factory(this.store, Roo.data);
7169 //        this.store.on('load', this.onLoad, this);
7170 //        
7171 //        this.store.load();
7172 //        
7173 //    },
7174 //    
7175 //    onLoad: function () 
7176 //    {   
7177 //        this.fireEvent('load', this);
7178 //    }
7179 //    
7180 //   
7181 });
7182
7183  
7184
7185  /*
7186  * Based on:
7187  * Ext JS Library 1.1.1
7188  * Copyright(c) 2006-2007, Ext JS, LLC.
7189  *
7190  * Originally Released Under LGPL - original licence link has changed is not relivant.
7191  *
7192  * Fork - LGPL
7193  * <script type="text/javascript">
7194  */
7195
7196 // as we use this in bootstrap.
7197 Roo.namespace('Roo.form');
7198  /**
7199  * @class Roo.form.Action
7200  * Internal Class used to handle form actions
7201  * @constructor
7202  * @param {Roo.form.BasicForm} el The form element or its id
7203  * @param {Object} config Configuration options
7204  */
7205
7206  
7207  
7208 // define the action interface
7209 Roo.form.Action = function(form, options){
7210     this.form = form;
7211     this.options = options || {};
7212 };
7213 /**
7214  * Client Validation Failed
7215  * @const 
7216  */
7217 Roo.form.Action.CLIENT_INVALID = 'client';
7218 /**
7219  * Server Validation Failed
7220  * @const 
7221  */
7222 Roo.form.Action.SERVER_INVALID = 'server';
7223  /**
7224  * Connect to Server Failed
7225  * @const 
7226  */
7227 Roo.form.Action.CONNECT_FAILURE = 'connect';
7228 /**
7229  * Reading Data from Server Failed
7230  * @const 
7231  */
7232 Roo.form.Action.LOAD_FAILURE = 'load';
7233
7234 Roo.form.Action.prototype = {
7235     type : 'default',
7236     failureType : undefined,
7237     response : undefined,
7238     result : undefined,
7239
7240     // interface method
7241     run : function(options){
7242
7243     },
7244
7245     // interface method
7246     success : function(response){
7247
7248     },
7249
7250     // interface method
7251     handleResponse : function(response){
7252
7253     },
7254
7255     // default connection failure
7256     failure : function(response){
7257         
7258         this.response = response;
7259         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7260         this.form.afterAction(this, false);
7261     },
7262
7263     processResponse : function(response){
7264         this.response = response;
7265         if(!response.responseText){
7266             return true;
7267         }
7268         this.result = this.handleResponse(response);
7269         return this.result;
7270     },
7271
7272     // utility functions used internally
7273     getUrl : function(appendParams){
7274         var url = this.options.url || this.form.url || this.form.el.dom.action;
7275         if(appendParams){
7276             var p = this.getParams();
7277             if(p){
7278                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7279             }
7280         }
7281         return url;
7282     },
7283
7284     getMethod : function(){
7285         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7286     },
7287
7288     getParams : function(){
7289         var bp = this.form.baseParams;
7290         var p = this.options.params;
7291         if(p){
7292             if(typeof p == "object"){
7293                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7294             }else if(typeof p == 'string' && bp){
7295                 p += '&' + Roo.urlEncode(bp);
7296             }
7297         }else if(bp){
7298             p = Roo.urlEncode(bp);
7299         }
7300         return p;
7301     },
7302
7303     createCallback : function(){
7304         return {
7305             success: this.success,
7306             failure: this.failure,
7307             scope: this,
7308             timeout: (this.form.timeout*1000),
7309             upload: this.form.fileUpload ? this.success : undefined
7310         };
7311     }
7312 };
7313
7314 Roo.form.Action.Submit = function(form, options){
7315     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7316 };
7317
7318 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7319     type : 'submit',
7320
7321     haveProgress : false,
7322     uploadComplete : false,
7323     
7324     // uploadProgress indicator.
7325     uploadProgress : function()
7326     {
7327         if (!this.form.progressUrl) {
7328             return;
7329         }
7330         
7331         if (!this.haveProgress) {
7332             Roo.MessageBox.progress("Uploading", "Uploading");
7333         }
7334         if (this.uploadComplete) {
7335            Roo.MessageBox.hide();
7336            return;
7337         }
7338         
7339         this.haveProgress = true;
7340    
7341         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7342         
7343         var c = new Roo.data.Connection();
7344         c.request({
7345             url : this.form.progressUrl,
7346             params: {
7347                 id : uid
7348             },
7349             method: 'GET',
7350             success : function(req){
7351                //console.log(data);
7352                 var rdata = false;
7353                 var edata;
7354                 try  {
7355                    rdata = Roo.decode(req.responseText)
7356                 } catch (e) {
7357                     Roo.log("Invalid data from server..");
7358                     Roo.log(edata);
7359                     return;
7360                 }
7361                 if (!rdata || !rdata.success) {
7362                     Roo.log(rdata);
7363                     Roo.MessageBox.alert(Roo.encode(rdata));
7364                     return;
7365                 }
7366                 var data = rdata.data;
7367                 
7368                 if (this.uploadComplete) {
7369                    Roo.MessageBox.hide();
7370                    return;
7371                 }
7372                    
7373                 if (data){
7374                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7375                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7376                     );
7377                 }
7378                 this.uploadProgress.defer(2000,this);
7379             },
7380        
7381             failure: function(data) {
7382                 Roo.log('progress url failed ');
7383                 Roo.log(data);
7384             },
7385             scope : this
7386         });
7387            
7388     },
7389     
7390     
7391     run : function()
7392     {
7393         // run get Values on the form, so it syncs any secondary forms.
7394         this.form.getValues();
7395         
7396         var o = this.options;
7397         var method = this.getMethod();
7398         var isPost = method == 'POST';
7399         if(o.clientValidation === false || this.form.isValid()){
7400             
7401             if (this.form.progressUrl) {
7402                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7403                     (new Date() * 1) + '' + Math.random());
7404                     
7405             } 
7406             
7407             
7408             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7409                 form:this.form.el.dom,
7410                 url:this.getUrl(!isPost),
7411                 method: method,
7412                 params:isPost ? this.getParams() : null,
7413                 isUpload: this.form.fileUpload
7414             }));
7415             
7416             this.uploadProgress();
7417
7418         }else if (o.clientValidation !== false){ // client validation failed
7419             this.failureType = Roo.form.Action.CLIENT_INVALID;
7420             this.form.afterAction(this, false);
7421         }
7422     },
7423
7424     success : function(response)
7425     {
7426         this.uploadComplete= true;
7427         if (this.haveProgress) {
7428             Roo.MessageBox.hide();
7429         }
7430         
7431         
7432         var result = this.processResponse(response);
7433         if(result === true || result.success){
7434             this.form.afterAction(this, true);
7435             return;
7436         }
7437         if(result.errors){
7438             this.form.markInvalid(result.errors);
7439             this.failureType = Roo.form.Action.SERVER_INVALID;
7440         }
7441         this.form.afterAction(this, false);
7442     },
7443     failure : function(response)
7444     {
7445         this.uploadComplete= true;
7446         if (this.haveProgress) {
7447             Roo.MessageBox.hide();
7448         }
7449         
7450         this.response = response;
7451         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7452         this.form.afterAction(this, false);
7453     },
7454     
7455     handleResponse : function(response){
7456         if(this.form.errorReader){
7457             var rs = this.form.errorReader.read(response);
7458             var errors = [];
7459             if(rs.records){
7460                 for(var i = 0, len = rs.records.length; i < len; i++) {
7461                     var r = rs.records[i];
7462                     errors[i] = r.data;
7463                 }
7464             }
7465             if(errors.length < 1){
7466                 errors = null;
7467             }
7468             return {
7469                 success : rs.success,
7470                 errors : errors
7471             };
7472         }
7473         var ret = false;
7474         try {
7475             ret = Roo.decode(response.responseText);
7476         } catch (e) {
7477             ret = {
7478                 success: false,
7479                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7480                 errors : []
7481             };
7482         }
7483         return ret;
7484         
7485     }
7486 });
7487
7488
7489 Roo.form.Action.Load = function(form, options){
7490     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7491     this.reader = this.form.reader;
7492 };
7493
7494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7495     type : 'load',
7496
7497     run : function(){
7498         
7499         Roo.Ajax.request(Roo.apply(
7500                 this.createCallback(), {
7501                     method:this.getMethod(),
7502                     url:this.getUrl(false),
7503                     params:this.getParams()
7504         }));
7505     },
7506
7507     success : function(response){
7508         
7509         var result = this.processResponse(response);
7510         if(result === true || !result.success || !result.data){
7511             this.failureType = Roo.form.Action.LOAD_FAILURE;
7512             this.form.afterAction(this, false);
7513             return;
7514         }
7515         this.form.clearInvalid();
7516         this.form.setValues(result.data);
7517         this.form.afterAction(this, true);
7518     },
7519
7520     handleResponse : function(response){
7521         if(this.form.reader){
7522             var rs = this.form.reader.read(response);
7523             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7524             return {
7525                 success : rs.success,
7526                 data : data
7527             };
7528         }
7529         return Roo.decode(response.responseText);
7530     }
7531 });
7532
7533 Roo.form.Action.ACTION_TYPES = {
7534     'load' : Roo.form.Action.Load,
7535     'submit' : Roo.form.Action.Submit
7536 };/*
7537  * - LGPL
7538  *
7539  * form
7540  *
7541  */
7542
7543 /**
7544  * @class Roo.bootstrap.Form
7545  * @extends Roo.bootstrap.Component
7546  * Bootstrap Form class
7547  * @cfg {String} method  GET | POST (default POST)
7548  * @cfg {String} labelAlign top | left (default top)
7549  * @cfg {String} align left  | right - for navbars
7550  * @cfg {Boolean} loadMask load mask when submit (default true)
7551
7552  *
7553  * @constructor
7554  * Create a new Form
7555  * @param {Object} config The config object
7556  */
7557
7558
7559 Roo.bootstrap.Form = function(config){
7560     
7561     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7562     
7563     Roo.bootstrap.Form.popover.apply();
7564     
7565     this.addEvents({
7566         /**
7567          * @event clientvalidation
7568          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7569          * @param {Form} this
7570          * @param {Boolean} valid true if the form has passed client-side validation
7571          */
7572         clientvalidation: true,
7573         /**
7574          * @event beforeaction
7575          * Fires before any action is performed. Return false to cancel the action.
7576          * @param {Form} this
7577          * @param {Action} action The action to be performed
7578          */
7579         beforeaction: true,
7580         /**
7581          * @event actionfailed
7582          * Fires when an action fails.
7583          * @param {Form} this
7584          * @param {Action} action The action that failed
7585          */
7586         actionfailed : true,
7587         /**
7588          * @event actioncomplete
7589          * Fires when an action is completed.
7590          * @param {Form} this
7591          * @param {Action} action The action that completed
7592          */
7593         actioncomplete : true
7594     });
7595 };
7596
7597 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7598
7599      /**
7600      * @cfg {String} method
7601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7602      */
7603     method : 'POST',
7604     /**
7605      * @cfg {String} url
7606      * The URL to use for form actions if one isn't supplied in the action options.
7607      */
7608     /**
7609      * @cfg {Boolean} fileUpload
7610      * Set to true if this form is a file upload.
7611      */
7612
7613     /**
7614      * @cfg {Object} baseParams
7615      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7616      */
7617
7618     /**
7619      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7620      */
7621     timeout: 30,
7622     /**
7623      * @cfg {Sting} align (left|right) for navbar forms
7624      */
7625     align : 'left',
7626
7627     // private
7628     activeAction : null,
7629
7630     /**
7631      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7632      * element by passing it or its id or mask the form itself by passing in true.
7633      * @type Mixed
7634      */
7635     waitMsgTarget : false,
7636
7637     loadMask : true,
7638     
7639     /**
7640      * @cfg {Boolean} errorMask (true|false) default false
7641      */
7642     errorMask : false,
7643     
7644     /**
7645      * @cfg {Number} maskOffset Default 100
7646      */
7647     maskOffset : 100,
7648     
7649     /**
7650      * @cfg {Boolean} maskBody
7651      */
7652     maskBody : false,
7653
7654     getAutoCreate : function(){
7655
7656         var cfg = {
7657             tag: 'form',
7658             method : this.method || 'POST',
7659             id : this.id || Roo.id(),
7660             cls : ''
7661         };
7662         if (this.parent().xtype.match(/^Nav/)) {
7663             cfg.cls = 'navbar-form navbar-' + this.align;
7664
7665         }
7666
7667         if (this.labelAlign == 'left' ) {
7668             cfg.cls += ' form-horizontal';
7669         }
7670
7671
7672         return cfg;
7673     },
7674     initEvents : function()
7675     {
7676         this.el.on('submit', this.onSubmit, this);
7677         // this was added as random key presses on the form where triggering form submit.
7678         this.el.on('keypress', function(e) {
7679             if (e.getCharCode() != 13) {
7680                 return true;
7681             }
7682             // we might need to allow it for textareas.. and some other items.
7683             // check e.getTarget().
7684
7685             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7686                 return true;
7687             }
7688
7689             Roo.log("keypress blocked");
7690
7691             e.preventDefault();
7692             return false;
7693         });
7694         
7695     },
7696     // private
7697     onSubmit : function(e){
7698         e.stopEvent();
7699     },
7700
7701      /**
7702      * Returns true if client-side validation on the form is successful.
7703      * @return Boolean
7704      */
7705     isValid : function(){
7706         var items = this.getItems();
7707         var valid = true;
7708         var target = false;
7709         
7710         items.each(function(f){
7711             if(f.validate()){
7712                 return;
7713             }
7714             valid = false;
7715
7716             if(!target && f.el.isVisible(true)){
7717                 target = f;
7718             }
7719            
7720         });
7721         
7722         if(this.errorMask && !valid){
7723             Roo.bootstrap.Form.popover.mask(this, target);
7724         }
7725         
7726         return valid;
7727     },
7728     
7729     /**
7730      * Returns true if any fields in this form have changed since their original load.
7731      * @return Boolean
7732      */
7733     isDirty : function(){
7734         var dirty = false;
7735         var items = this.getItems();
7736         items.each(function(f){
7737            if(f.isDirty()){
7738                dirty = true;
7739                return false;
7740            }
7741            return true;
7742         });
7743         return dirty;
7744     },
7745      /**
7746      * Performs a predefined action (submit or load) or custom actions you define on this form.
7747      * @param {String} actionName The name of the action type
7748      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7749      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7750      * accept other config options):
7751      * <pre>
7752 Property          Type             Description
7753 ----------------  ---------------  ----------------------------------------------------------------------------------
7754 url               String           The url for the action (defaults to the form's url)
7755 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7756 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7757 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7758                                    validate the form on the client (defaults to false)
7759      * </pre>
7760      * @return {BasicForm} this
7761      */
7762     doAction : function(action, options){
7763         if(typeof action == 'string'){
7764             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7765         }
7766         if(this.fireEvent('beforeaction', this, action) !== false){
7767             this.beforeAction(action);
7768             action.run.defer(100, action);
7769         }
7770         return this;
7771     },
7772
7773     // private
7774     beforeAction : function(action){
7775         var o = action.options;
7776         
7777         if(this.loadMask){
7778             
7779             if(this.maskBody){
7780                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7781             } else {
7782                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7783             }
7784         }
7785         // not really supported yet.. ??
7786
7787         //if(this.waitMsgTarget === true){
7788         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7789         //}else if(this.waitMsgTarget){
7790         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7791         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7792         //}else {
7793         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7794        // }
7795
7796     },
7797
7798     // private
7799     afterAction : function(action, success){
7800         this.activeAction = null;
7801         var o = action.options;
7802
7803         if(this.loadMask){
7804             
7805             if(this.maskBody){
7806                 Roo.get(document.body).unmask();
7807             } else {
7808                 this.el.unmask();
7809             }
7810         }
7811         
7812         //if(this.waitMsgTarget === true){
7813 //            this.el.unmask();
7814         //}else if(this.waitMsgTarget){
7815         //    this.waitMsgTarget.unmask();
7816         //}else{
7817         //    Roo.MessageBox.updateProgress(1);
7818         //    Roo.MessageBox.hide();
7819        // }
7820         //
7821         if(success){
7822             if(o.reset){
7823                 this.reset();
7824             }
7825             Roo.callback(o.success, o.scope, [this, action]);
7826             this.fireEvent('actioncomplete', this, action);
7827
7828         }else{
7829
7830             // failure condition..
7831             // we have a scenario where updates need confirming.
7832             // eg. if a locking scenario exists..
7833             // we look for { errors : { needs_confirm : true }} in the response.
7834             if (
7835                 (typeof(action.result) != 'undefined')  &&
7836                 (typeof(action.result.errors) != 'undefined')  &&
7837                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7838            ){
7839                 var _t = this;
7840                 Roo.log("not supported yet");
7841                  /*
7842
7843                 Roo.MessageBox.confirm(
7844                     "Change requires confirmation",
7845                     action.result.errorMsg,
7846                     function(r) {
7847                         if (r != 'yes') {
7848                             return;
7849                         }
7850                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7851                     }
7852
7853                 );
7854                 */
7855
7856
7857                 return;
7858             }
7859
7860             Roo.callback(o.failure, o.scope, [this, action]);
7861             // show an error message if no failed handler is set..
7862             if (!this.hasListener('actionfailed')) {
7863                 Roo.log("need to add dialog support");
7864                 /*
7865                 Roo.MessageBox.alert("Error",
7866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7867                         action.result.errorMsg :
7868                         "Saving Failed, please check your entries or try again"
7869                 );
7870                 */
7871             }
7872
7873             this.fireEvent('actionfailed', this, action);
7874         }
7875
7876     },
7877     /**
7878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7879      * @param {String} id The value to search for
7880      * @return Field
7881      */
7882     findField : function(id){
7883         var items = this.getItems();
7884         var field = items.get(id);
7885         if(!field){
7886              items.each(function(f){
7887                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7888                     field = f;
7889                     return false;
7890                 }
7891                 return true;
7892             });
7893         }
7894         return field || null;
7895     },
7896      /**
7897      * Mark fields in this form invalid in bulk.
7898      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7899      * @return {BasicForm} this
7900      */
7901     markInvalid : function(errors){
7902         if(errors instanceof Array){
7903             for(var i = 0, len = errors.length; i < len; i++){
7904                 var fieldError = errors[i];
7905                 var f = this.findField(fieldError.id);
7906                 if(f){
7907                     f.markInvalid(fieldError.msg);
7908                 }
7909             }
7910         }else{
7911             var field, id;
7912             for(id in errors){
7913                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7914                     field.markInvalid(errors[id]);
7915                 }
7916             }
7917         }
7918         //Roo.each(this.childForms || [], function (f) {
7919         //    f.markInvalid(errors);
7920         //});
7921
7922         return this;
7923     },
7924
7925     /**
7926      * Set values for fields in this form in bulk.
7927      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7928      * @return {BasicForm} this
7929      */
7930     setValues : function(values){
7931         if(values instanceof Array){ // array of objects
7932             for(var i = 0, len = values.length; i < len; i++){
7933                 var v = values[i];
7934                 var f = this.findField(v.id);
7935                 if(f){
7936                     f.setValue(v.value);
7937                     if(this.trackResetOnLoad){
7938                         f.originalValue = f.getValue();
7939                     }
7940                 }
7941             }
7942         }else{ // object hash
7943             var field, id;
7944             for(id in values){
7945                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7946
7947                     if (field.setFromData &&
7948                         field.valueField &&
7949                         field.displayField &&
7950                         // combos' with local stores can
7951                         // be queried via setValue()
7952                         // to set their value..
7953                         (field.store && !field.store.isLocal)
7954                         ) {
7955                         // it's a combo
7956                         var sd = { };
7957                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7958                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7959                         field.setFromData(sd);
7960
7961                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7962                         
7963                         field.setFromData(values);
7964                         
7965                     } else {
7966                         field.setValue(values[id]);
7967                     }
7968
7969
7970                     if(this.trackResetOnLoad){
7971                         field.originalValue = field.getValue();
7972                     }
7973                 }
7974             }
7975         }
7976
7977         //Roo.each(this.childForms || [], function (f) {
7978         //    f.setValues(values);
7979         //});
7980
7981         return this;
7982     },
7983
7984     /**
7985      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7986      * they are returned as an array.
7987      * @param {Boolean} asString
7988      * @return {Object}
7989      */
7990     getValues : function(asString){
7991         //if (this.childForms) {
7992             // copy values from the child forms
7993         //    Roo.each(this.childForms, function (f) {
7994         //        this.setValues(f.getValues());
7995         //    }, this);
7996         //}
7997
7998
7999
8000         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8001         if(asString === true){
8002             return fs;
8003         }
8004         return Roo.urlDecode(fs);
8005     },
8006
8007     /**
8008      * Returns the fields in this form as an object with key/value pairs.
8009      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8010      * @return {Object}
8011      */
8012     getFieldValues : function(with_hidden)
8013     {
8014         var items = this.getItems();
8015         var ret = {};
8016         items.each(function(f){
8017             
8018             if (!f.getName()) {
8019                 return;
8020             }
8021             
8022             var v = f.getValue();
8023             
8024             if (f.inputType =='radio') {
8025                 if (typeof(ret[f.getName()]) == 'undefined') {
8026                     ret[f.getName()] = ''; // empty..
8027                 }
8028
8029                 if (!f.el.dom.checked) {
8030                     return;
8031
8032                 }
8033                 v = f.el.dom.value;
8034
8035             }
8036             
8037             if(f.xtype == 'MoneyField'){
8038                 ret[f.currencyName] = f.getCurrency();
8039             }
8040
8041             // not sure if this supported any more..
8042             if ((typeof(v) == 'object') && f.getRawValue) {
8043                 v = f.getRawValue() ; // dates..
8044             }
8045             // combo boxes where name != hiddenName...
8046             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8047                 ret[f.name] = f.getRawValue();
8048             }
8049             ret[f.getName()] = v;
8050         });
8051
8052         return ret;
8053     },
8054
8055     /**
8056      * Clears all invalid messages in this form.
8057      * @return {BasicForm} this
8058      */
8059     clearInvalid : function(){
8060         var items = this.getItems();
8061
8062         items.each(function(f){
8063            f.clearInvalid();
8064         });
8065
8066         return this;
8067     },
8068
8069     /**
8070      * Resets this form.
8071      * @return {BasicForm} this
8072      */
8073     reset : function(){
8074         var items = this.getItems();
8075         items.each(function(f){
8076             f.reset();
8077         });
8078
8079         Roo.each(this.childForms || [], function (f) {
8080             f.reset();
8081         });
8082
8083
8084         return this;
8085     },
8086     
8087     getItems : function()
8088     {
8089         var r=new Roo.util.MixedCollection(false, function(o){
8090             return o.id || (o.id = Roo.id());
8091         });
8092         var iter = function(el) {
8093             if (el.inputEl) {
8094                 r.add(el);
8095             }
8096             if (!el.items) {
8097                 return;
8098             }
8099             Roo.each(el.items,function(e) {
8100                 iter(e);
8101             });
8102         };
8103
8104         iter(this);
8105         return r;
8106     }
8107
8108 });
8109
8110 Roo.apply(Roo.bootstrap.Form, {
8111     
8112     popover : {
8113         
8114         padding : 5,
8115         
8116         isApplied : false,
8117         
8118         isMasked : false,
8119         
8120         form : false,
8121         
8122         target : false,
8123         
8124         toolTip : false,
8125         
8126         intervalID : false,
8127         
8128         maskEl : false,
8129         
8130         apply : function()
8131         {
8132             if(this.isApplied){
8133                 return;
8134             }
8135             
8136             this.maskEl = {
8137                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8138                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8139                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8140                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8141             };
8142             
8143             this.maskEl.top.enableDisplayMode("block");
8144             this.maskEl.left.enableDisplayMode("block");
8145             this.maskEl.bottom.enableDisplayMode("block");
8146             this.maskEl.right.enableDisplayMode("block");
8147             
8148             this.toolTip = new Roo.bootstrap.Tooltip({
8149                 cls : 'roo-form-error-popover',
8150                 alignment : {
8151                     'left' : ['r-l', [-2,0], 'right'],
8152                     'right' : ['l-r', [2,0], 'left'],
8153                     'bottom' : ['tl-bl', [0,2], 'top'],
8154                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8155                 }
8156             });
8157             
8158             this.toolTip.render(Roo.get(document.body));
8159
8160             this.toolTip.el.enableDisplayMode("block");
8161             
8162             Roo.get(document.body).on('click', function(){
8163                 this.unmask();
8164             }, this);
8165             
8166             Roo.get(document.body).on('touchstart', function(){
8167                 this.unmask();
8168             }, this);
8169             
8170             this.isApplied = true
8171         },
8172         
8173         mask : function(form, target)
8174         {
8175             this.form = form;
8176             
8177             this.target = target;
8178             
8179             if(!this.form.errorMask || !target.el){
8180                 return;
8181             }
8182             
8183             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8184             
8185             Roo.log(scrollable);
8186             
8187             var ot = this.target.el.calcOffsetsTo(scrollable);
8188             
8189             var scrollTo = ot[1] - this.form.maskOffset;
8190             
8191             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8192             
8193             scrollable.scrollTo('top', scrollTo);
8194             
8195             var box = this.target.el.getBox();
8196             Roo.log(box);
8197             var zIndex = Roo.bootstrap.Modal.zIndex++;
8198
8199             
8200             this.maskEl.top.setStyle('position', 'absolute');
8201             this.maskEl.top.setStyle('z-index', zIndex);
8202             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8203             this.maskEl.top.setLeft(0);
8204             this.maskEl.top.setTop(0);
8205             this.maskEl.top.show();
8206             
8207             this.maskEl.left.setStyle('position', 'absolute');
8208             this.maskEl.left.setStyle('z-index', zIndex);
8209             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8210             this.maskEl.left.setLeft(0);
8211             this.maskEl.left.setTop(box.y - this.padding);
8212             this.maskEl.left.show();
8213
8214             this.maskEl.bottom.setStyle('position', 'absolute');
8215             this.maskEl.bottom.setStyle('z-index', zIndex);
8216             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8217             this.maskEl.bottom.setLeft(0);
8218             this.maskEl.bottom.setTop(box.bottom + this.padding);
8219             this.maskEl.bottom.show();
8220
8221             this.maskEl.right.setStyle('position', 'absolute');
8222             this.maskEl.right.setStyle('z-index', zIndex);
8223             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8224             this.maskEl.right.setLeft(box.right + this.padding);
8225             this.maskEl.right.setTop(box.y - this.padding);
8226             this.maskEl.right.show();
8227
8228             this.toolTip.bindEl = this.target.el;
8229
8230             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8231
8232             var tip = this.target.blankText;
8233
8234             if(this.target.getValue() !== '' ) {
8235                 
8236                 if (this.target.invalidText.length) {
8237                     tip = this.target.invalidText;
8238                 } else if (this.target.regexText.length){
8239                     tip = this.target.regexText;
8240                 }
8241             }
8242
8243             this.toolTip.show(tip);
8244
8245             this.intervalID = window.setInterval(function() {
8246                 Roo.bootstrap.Form.popover.unmask();
8247             }, 10000);
8248
8249             window.onwheel = function(){ return false;};
8250             
8251             (function(){ this.isMasked = true; }).defer(500, this);
8252             
8253         },
8254         
8255         unmask : function()
8256         {
8257             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8258                 return;
8259             }
8260             
8261             this.maskEl.top.setStyle('position', 'absolute');
8262             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8263             this.maskEl.top.hide();
8264
8265             this.maskEl.left.setStyle('position', 'absolute');
8266             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8267             this.maskEl.left.hide();
8268
8269             this.maskEl.bottom.setStyle('position', 'absolute');
8270             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8271             this.maskEl.bottom.hide();
8272
8273             this.maskEl.right.setStyle('position', 'absolute');
8274             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8275             this.maskEl.right.hide();
8276             
8277             this.toolTip.hide();
8278             
8279             this.toolTip.el.hide();
8280             
8281             window.onwheel = function(){ return true;};
8282             
8283             if(this.intervalID){
8284                 window.clearInterval(this.intervalID);
8285                 this.intervalID = false;
8286             }
8287             
8288             this.isMasked = false;
8289             
8290         }
8291         
8292     }
8293     
8294 });
8295
8296 /*
8297  * Based on:
8298  * Ext JS Library 1.1.1
8299  * Copyright(c) 2006-2007, Ext JS, LLC.
8300  *
8301  * Originally Released Under LGPL - original licence link has changed is not relivant.
8302  *
8303  * Fork - LGPL
8304  * <script type="text/javascript">
8305  */
8306 /**
8307  * @class Roo.form.VTypes
8308  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8309  * @singleton
8310  */
8311 Roo.form.VTypes = function(){
8312     // closure these in so they are only created once.
8313     var alpha = /^[a-zA-Z_]+$/;
8314     var alphanum = /^[a-zA-Z0-9_]+$/;
8315     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8316     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8317
8318     // All these messages and functions are configurable
8319     return {
8320         /**
8321          * The function used to validate email addresses
8322          * @param {String} value The email address
8323          */
8324         'email' : function(v){
8325             return email.test(v);
8326         },
8327         /**
8328          * The error text to display when the email validation function returns false
8329          * @type String
8330          */
8331         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8332         /**
8333          * The keystroke filter mask to be applied on email input
8334          * @type RegExp
8335          */
8336         'emailMask' : /[a-z0-9_\.\-@]/i,
8337
8338         /**
8339          * The function used to validate URLs
8340          * @param {String} value The URL
8341          */
8342         'url' : function(v){
8343             return url.test(v);
8344         },
8345         /**
8346          * The error text to display when the url validation function returns false
8347          * @type String
8348          */
8349         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8350         
8351         /**
8352          * The function used to validate alpha values
8353          * @param {String} value The value
8354          */
8355         'alpha' : function(v){
8356             return alpha.test(v);
8357         },
8358         /**
8359          * The error text to display when the alpha validation function returns false
8360          * @type String
8361          */
8362         'alphaText' : 'This field should only contain letters and _',
8363         /**
8364          * The keystroke filter mask to be applied on alpha input
8365          * @type RegExp
8366          */
8367         'alphaMask' : /[a-z_]/i,
8368
8369         /**
8370          * The function used to validate alphanumeric values
8371          * @param {String} value The value
8372          */
8373         'alphanum' : function(v){
8374             return alphanum.test(v);
8375         },
8376         /**
8377          * The error text to display when the alphanumeric validation function returns false
8378          * @type String
8379          */
8380         'alphanumText' : 'This field should only contain letters, numbers and _',
8381         /**
8382          * The keystroke filter mask to be applied on alphanumeric input
8383          * @type RegExp
8384          */
8385         'alphanumMask' : /[a-z0-9_]/i
8386     };
8387 }();/*
8388  * - LGPL
8389  *
8390  * Input
8391  * 
8392  */
8393
8394 /**
8395  * @class Roo.bootstrap.Input
8396  * @extends Roo.bootstrap.Component
8397  * Bootstrap Input class
8398  * @cfg {Boolean} disabled is it disabled
8399  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8400  * @cfg {String} name name of the input
8401  * @cfg {string} fieldLabel - the label associated
8402  * @cfg {string} placeholder - placeholder to put in text.
8403  * @cfg {string}  before - input group add on before
8404  * @cfg {string} after - input group add on after
8405  * @cfg {string} size - (lg|sm) or leave empty..
8406  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8407  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8408  * @cfg {Number} md colspan out of 12 for computer-sized screens
8409  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8410  * @cfg {string} value default value of the input
8411  * @cfg {Number} labelWidth set the width of label 
8412  * @cfg {Number} labellg set the width of label (1-12)
8413  * @cfg {Number} labelmd set the width of label (1-12)
8414  * @cfg {Number} labelsm set the width of label (1-12)
8415  * @cfg {Number} labelxs set the width of label (1-12)
8416  * @cfg {String} labelAlign (top|left)
8417  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8418  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8419  * @cfg {String} indicatorpos (left|right) default left
8420
8421  * @cfg {String} align (left|center|right) Default left
8422  * @cfg {Boolean} forceFeedback (true|false) Default false
8423  * 
8424  * 
8425  * 
8426  * 
8427  * @constructor
8428  * Create a new Input
8429  * @param {Object} config The config object
8430  */
8431
8432 Roo.bootstrap.Input = function(config){
8433     
8434     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8435     
8436     this.addEvents({
8437         /**
8438          * @event focus
8439          * Fires when this field receives input focus.
8440          * @param {Roo.form.Field} this
8441          */
8442         focus : true,
8443         /**
8444          * @event blur
8445          * Fires when this field loses input focus.
8446          * @param {Roo.form.Field} this
8447          */
8448         blur : true,
8449         /**
8450          * @event specialkey
8451          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8452          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8453          * @param {Roo.form.Field} this
8454          * @param {Roo.EventObject} e The event object
8455          */
8456         specialkey : true,
8457         /**
8458          * @event change
8459          * Fires just before the field blurs if the field value has changed.
8460          * @param {Roo.form.Field} this
8461          * @param {Mixed} newValue The new value
8462          * @param {Mixed} oldValue The original value
8463          */
8464         change : true,
8465         /**
8466          * @event invalid
8467          * Fires after the field has been marked as invalid.
8468          * @param {Roo.form.Field} this
8469          * @param {String} msg The validation message
8470          */
8471         invalid : true,
8472         /**
8473          * @event valid
8474          * Fires after the field has been validated with no errors.
8475          * @param {Roo.form.Field} this
8476          */
8477         valid : true,
8478          /**
8479          * @event keyup
8480          * Fires after the key up
8481          * @param {Roo.form.Field} this
8482          * @param {Roo.EventObject}  e The event Object
8483          */
8484         keyup : true
8485     });
8486 };
8487
8488 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8489      /**
8490      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8491       automatic validation (defaults to "keyup").
8492      */
8493     validationEvent : "keyup",
8494      /**
8495      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8496      */
8497     validateOnBlur : true,
8498     /**
8499      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8500      */
8501     validationDelay : 250,
8502      /**
8503      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8504      */
8505     focusClass : "x-form-focus",  // not needed???
8506     
8507        
8508     /**
8509      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8510      */
8511     invalidClass : "has-warning",
8512     
8513     /**
8514      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8515      */
8516     validClass : "has-success",
8517     
8518     /**
8519      * @cfg {Boolean} hasFeedback (true|false) default true
8520      */
8521     hasFeedback : true,
8522     
8523     /**
8524      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8525      */
8526     invalidFeedbackClass : "glyphicon-warning-sign",
8527     
8528     /**
8529      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8530      */
8531     validFeedbackClass : "glyphicon-ok",
8532     
8533     /**
8534      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8535      */
8536     selectOnFocus : false,
8537     
8538      /**
8539      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8540      */
8541     maskRe : null,
8542        /**
8543      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8544      */
8545     vtype : null,
8546     
8547       /**
8548      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8549      */
8550     disableKeyFilter : false,
8551     
8552        /**
8553      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8554      */
8555     disabled : false,
8556      /**
8557      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8558      */
8559     allowBlank : true,
8560     /**
8561      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8562      */
8563     blankText : "Please complete this mandatory field",
8564     
8565      /**
8566      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8567      */
8568     minLength : 0,
8569     /**
8570      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8571      */
8572     maxLength : Number.MAX_VALUE,
8573     /**
8574      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8575      */
8576     minLengthText : "The minimum length for this field is {0}",
8577     /**
8578      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8579      */
8580     maxLengthText : "The maximum length for this field is {0}",
8581   
8582     
8583     /**
8584      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8585      * If available, this function will be called only after the basic validators all return true, and will be passed the
8586      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8587      */
8588     validator : null,
8589     /**
8590      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8591      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8592      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8593      */
8594     regex : null,
8595     /**
8596      * @cfg {String} regexText -- Depricated - use Invalid Text
8597      */
8598     regexText : "",
8599     
8600     /**
8601      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8602      */
8603     invalidText : "",
8604     
8605     
8606     
8607     autocomplete: false,
8608     
8609     
8610     fieldLabel : '',
8611     inputType : 'text',
8612     
8613     name : false,
8614     placeholder: false,
8615     before : false,
8616     after : false,
8617     size : false,
8618     hasFocus : false,
8619     preventMark: false,
8620     isFormField : true,
8621     value : '',
8622     labelWidth : 2,
8623     labelAlign : false,
8624     readOnly : false,
8625     align : false,
8626     formatedValue : false,
8627     forceFeedback : false,
8628     
8629     indicatorpos : 'left',
8630     
8631     labellg : 0,
8632     labelmd : 0,
8633     labelsm : 0,
8634     labelxs : 0,
8635     
8636     parentLabelAlign : function()
8637     {
8638         var parent = this;
8639         while (parent.parent()) {
8640             parent = parent.parent();
8641             if (typeof(parent.labelAlign) !='undefined') {
8642                 return parent.labelAlign;
8643             }
8644         }
8645         return 'left';
8646         
8647     },
8648     
8649     getAutoCreate : function()
8650     {
8651         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8652         
8653         var id = Roo.id();
8654         
8655         var cfg = {};
8656         
8657         if(this.inputType != 'hidden'){
8658             cfg.cls = 'form-group' //input-group
8659         }
8660         
8661         var input =  {
8662             tag: 'input',
8663             id : id,
8664             type : this.inputType,
8665             value : this.value,
8666             cls : 'form-control',
8667             placeholder : this.placeholder || '',
8668             autocomplete : this.autocomplete || 'new-password'
8669         };
8670         
8671         if(this.align){
8672             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8673         }
8674         
8675         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8676             input.maxLength = this.maxLength;
8677         }
8678         
8679         if (this.disabled) {
8680             input.disabled=true;
8681         }
8682         
8683         if (this.readOnly) {
8684             input.readonly=true;
8685         }
8686         
8687         if (this.name) {
8688             input.name = this.name;
8689         }
8690         
8691         if (this.size) {
8692             input.cls += ' input-' + this.size;
8693         }
8694         
8695         var settings=this;
8696         ['xs','sm','md','lg'].map(function(size){
8697             if (settings[size]) {
8698                 cfg.cls += ' col-' + size + '-' + settings[size];
8699             }
8700         });
8701         
8702         var inputblock = input;
8703         
8704         var feedback = {
8705             tag: 'span',
8706             cls: 'glyphicon form-control-feedback'
8707         };
8708             
8709         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8710             
8711             inputblock = {
8712                 cls : 'has-feedback',
8713                 cn :  [
8714                     input,
8715                     feedback
8716                 ] 
8717             };  
8718         }
8719         
8720         if (this.before || this.after) {
8721             
8722             inputblock = {
8723                 cls : 'input-group',
8724                 cn :  [] 
8725             };
8726             
8727             if (this.before && typeof(this.before) == 'string') {
8728                 
8729                 inputblock.cn.push({
8730                     tag :'span',
8731                     cls : 'roo-input-before input-group-addon',
8732                     html : this.before
8733                 });
8734             }
8735             if (this.before && typeof(this.before) == 'object') {
8736                 this.before = Roo.factory(this.before);
8737                 
8738                 inputblock.cn.push({
8739                     tag :'span',
8740                     cls : 'roo-input-before input-group-' +
8741                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8742                 });
8743             }
8744             
8745             inputblock.cn.push(input);
8746             
8747             if (this.after && typeof(this.after) == 'string') {
8748                 inputblock.cn.push({
8749                     tag :'span',
8750                     cls : 'roo-input-after input-group-addon',
8751                     html : this.after
8752                 });
8753             }
8754             if (this.after && typeof(this.after) == 'object') {
8755                 this.after = Roo.factory(this.after);
8756                 
8757                 inputblock.cn.push({
8758                     tag :'span',
8759                     cls : 'roo-input-after input-group-' +
8760                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8761                 });
8762             }
8763             
8764             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8765                 inputblock.cls += ' has-feedback';
8766                 inputblock.cn.push(feedback);
8767             }
8768         };
8769         
8770         if (align ==='left' && this.fieldLabel.length) {
8771             
8772             cfg.cls += ' roo-form-group-label-left';
8773             
8774             cfg.cn = [
8775                 {
8776                     tag : 'i',
8777                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8778                     tooltip : 'This field is required'
8779                 },
8780                 {
8781                     tag: 'label',
8782                     'for' :  id,
8783                     cls : 'control-label',
8784                     html : this.fieldLabel
8785
8786                 },
8787                 {
8788                     cls : "", 
8789                     cn: [
8790                         inputblock
8791                     ]
8792                 }
8793             ];
8794             
8795             var labelCfg = cfg.cn[1];
8796             var contentCfg = cfg.cn[2];
8797             
8798             if(this.indicatorpos == 'right'){
8799                 cfg.cn = [
8800                     {
8801                         tag: 'label',
8802                         'for' :  id,
8803                         cls : 'control-label',
8804                         cn : [
8805                             {
8806                                 tag : 'span',
8807                                 html : this.fieldLabel
8808                             },
8809                             {
8810                                 tag : 'i',
8811                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8812                                 tooltip : 'This field is required'
8813                             }
8814                         ]
8815                     },
8816                     {
8817                         cls : "",
8818                         cn: [
8819                             inputblock
8820                         ]
8821                     }
8822
8823                 ];
8824                 
8825                 labelCfg = cfg.cn[0];
8826                 contentCfg = cfg.cn[1];
8827             
8828             }
8829             
8830             if(this.labelWidth > 12){
8831                 labelCfg.style = "width: " + this.labelWidth + 'px';
8832             }
8833             
8834             if(this.labelWidth < 13 && this.labelmd == 0){
8835                 this.labelmd = this.labelWidth;
8836             }
8837             
8838             if(this.labellg > 0){
8839                 labelCfg.cls += ' col-lg-' + this.labellg;
8840                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8841             }
8842             
8843             if(this.labelmd > 0){
8844                 labelCfg.cls += ' col-md-' + this.labelmd;
8845                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8846             }
8847             
8848             if(this.labelsm > 0){
8849                 labelCfg.cls += ' col-sm-' + this.labelsm;
8850                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8851             }
8852             
8853             if(this.labelxs > 0){
8854                 labelCfg.cls += ' col-xs-' + this.labelxs;
8855                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8856             }
8857             
8858             
8859         } else if ( this.fieldLabel.length) {
8860                 
8861             cfg.cn = [
8862                 {
8863                     tag : 'i',
8864                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8865                     tooltip : 'This field is required'
8866                 },
8867                 {
8868                     tag: 'label',
8869                    //cls : 'input-group-addon',
8870                     html : this.fieldLabel
8871
8872                 },
8873
8874                inputblock
8875
8876            ];
8877            
8878            if(this.indicatorpos == 'right'){
8879                 
8880                 cfg.cn = [
8881                     {
8882                         tag: 'label',
8883                        //cls : 'input-group-addon',
8884                         html : this.fieldLabel
8885
8886                     },
8887                     {
8888                         tag : 'i',
8889                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8890                         tooltip : 'This field is required'
8891                     },
8892
8893                    inputblock
8894
8895                ];
8896
8897             }
8898
8899         } else {
8900             
8901             cfg.cn = [
8902
8903                     inputblock
8904
8905             ];
8906                 
8907                 
8908         };
8909         
8910         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8911            cfg.cls += ' navbar-form';
8912         }
8913         
8914         if (this.parentType === 'NavGroup') {
8915            cfg.cls += ' navbar-form';
8916            cfg.tag = 'li';
8917         }
8918         
8919         return cfg;
8920         
8921     },
8922     /**
8923      * return the real input element.
8924      */
8925     inputEl: function ()
8926     {
8927         return this.el.select('input.form-control',true).first();
8928     },
8929     
8930     tooltipEl : function()
8931     {
8932         return this.inputEl();
8933     },
8934     
8935     indicatorEl : function()
8936     {
8937         var indicator = this.el.select('i.roo-required-indicator',true).first();
8938         
8939         if(!indicator){
8940             return false;
8941         }
8942         
8943         return indicator;
8944         
8945     },
8946     
8947     setDisabled : function(v)
8948     {
8949         var i  = this.inputEl().dom;
8950         if (!v) {
8951             i.removeAttribute('disabled');
8952             return;
8953             
8954         }
8955         i.setAttribute('disabled','true');
8956     },
8957     initEvents : function()
8958     {
8959           
8960         this.inputEl().on("keydown" , this.fireKey,  this);
8961         this.inputEl().on("focus", this.onFocus,  this);
8962         this.inputEl().on("blur", this.onBlur,  this);
8963         
8964         this.inputEl().relayEvent('keyup', this);
8965         
8966         this.indicator = this.indicatorEl();
8967         
8968         if(this.indicator){
8969             this.indicator.addClass('invisible');
8970             
8971         }
8972  
8973         // reference to original value for reset
8974         this.originalValue = this.getValue();
8975         //Roo.form.TextField.superclass.initEvents.call(this);
8976         if(this.validationEvent == 'keyup'){
8977             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8978             this.inputEl().on('keyup', this.filterValidation, this);
8979         }
8980         else if(this.validationEvent !== false){
8981             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8982         }
8983         
8984         if(this.selectOnFocus){
8985             this.on("focus", this.preFocus, this);
8986             
8987         }
8988         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8989             this.inputEl().on("keypress", this.filterKeys, this);
8990         } else {
8991             this.inputEl().relayEvent('keypress', this);
8992         }
8993        /* if(this.grow){
8994             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8995             this.el.on("click", this.autoSize,  this);
8996         }
8997         */
8998         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8999             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9000         }
9001         
9002         if (typeof(this.before) == 'object') {
9003             this.before.render(this.el.select('.roo-input-before',true).first());
9004         }
9005         if (typeof(this.after) == 'object') {
9006             this.after.render(this.el.select('.roo-input-after',true).first());
9007         }
9008         
9009         
9010     },
9011     filterValidation : function(e){
9012         if(!e.isNavKeyPress()){
9013             this.validationTask.delay(this.validationDelay);
9014         }
9015     },
9016      /**
9017      * Validates the field value
9018      * @return {Boolean} True if the value is valid, else false
9019      */
9020     validate : function(){
9021         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9022         if(this.disabled || this.validateValue(this.getRawValue())){
9023             this.markValid();
9024             return true;
9025         }
9026         
9027         this.markInvalid();
9028         return false;
9029     },
9030     
9031     
9032     /**
9033      * Validates a value according to the field's validation rules and marks the field as invalid
9034      * if the validation fails
9035      * @param {Mixed} value The value to validate
9036      * @return {Boolean} True if the value is valid, else false
9037      */
9038     validateValue : function(value){
9039         if(value.length < 1)  { // if it's blank
9040             if(this.allowBlank){
9041                 return true;
9042             }            
9043             return this.inputEl().hasClass('hide') ? true : false;
9044         }
9045         
9046         if(value.length < this.minLength){
9047             return false;
9048         }
9049         if(value.length > this.maxLength){
9050             return false;
9051         }
9052         if(this.vtype){
9053             var vt = Roo.form.VTypes;
9054             if(!vt[this.vtype](value, this)){
9055                 return false;
9056             }
9057         }
9058         if(typeof this.validator == "function"){
9059             var msg = this.validator(value);
9060             if(msg !== true){
9061                 return false;
9062             }
9063             if (typeof(msg) == 'string') {
9064                 this.invalidText = msg;
9065             }
9066         }
9067         
9068         if(this.regex && !this.regex.test(value)){
9069             return false;
9070         }
9071         
9072         return true;
9073     },
9074
9075     
9076     
9077      // private
9078     fireKey : function(e){
9079         //Roo.log('field ' + e.getKey());
9080         if(e.isNavKeyPress()){
9081             this.fireEvent("specialkey", this, e);
9082         }
9083     },
9084     focus : function (selectText){
9085         if(this.rendered){
9086             this.inputEl().focus();
9087             if(selectText === true){
9088                 this.inputEl().dom.select();
9089             }
9090         }
9091         return this;
9092     } ,
9093     
9094     onFocus : function(){
9095         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9096            // this.el.addClass(this.focusClass);
9097         }
9098         if(!this.hasFocus){
9099             this.hasFocus = true;
9100             this.startValue = this.getValue();
9101             this.fireEvent("focus", this);
9102         }
9103     },
9104     
9105     beforeBlur : Roo.emptyFn,
9106
9107     
9108     // private
9109     onBlur : function(){
9110         this.beforeBlur();
9111         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9112             //this.el.removeClass(this.focusClass);
9113         }
9114         this.hasFocus = false;
9115         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9116             this.validate();
9117         }
9118         var v = this.getValue();
9119         if(String(v) !== String(this.startValue)){
9120             this.fireEvent('change', this, v, this.startValue);
9121         }
9122         this.fireEvent("blur", this);
9123     },
9124     
9125     /**
9126      * Resets the current field value to the originally loaded value and clears any validation messages
9127      */
9128     reset : function(){
9129         this.setValue(this.originalValue);
9130         this.validate();
9131     },
9132      /**
9133      * Returns the name of the field
9134      * @return {Mixed} name The name field
9135      */
9136     getName: function(){
9137         return this.name;
9138     },
9139      /**
9140      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9141      * @return {Mixed} value The field value
9142      */
9143     getValue : function(){
9144         
9145         var v = this.inputEl().getValue();
9146         
9147         return v;
9148     },
9149     /**
9150      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9151      * @return {Mixed} value The field value
9152      */
9153     getRawValue : function(){
9154         var v = this.inputEl().getValue();
9155         
9156         return v;
9157     },
9158     
9159     /**
9160      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9161      * @param {Mixed} value The value to set
9162      */
9163     setRawValue : function(v){
9164         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9165     },
9166     
9167     selectText : function(start, end){
9168         var v = this.getRawValue();
9169         if(v.length > 0){
9170             start = start === undefined ? 0 : start;
9171             end = end === undefined ? v.length : end;
9172             var d = this.inputEl().dom;
9173             if(d.setSelectionRange){
9174                 d.setSelectionRange(start, end);
9175             }else if(d.createTextRange){
9176                 var range = d.createTextRange();
9177                 range.moveStart("character", start);
9178                 range.moveEnd("character", v.length-end);
9179                 range.select();
9180             }
9181         }
9182     },
9183     
9184     /**
9185      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9186      * @param {Mixed} value The value to set
9187      */
9188     setValue : function(v){
9189         this.value = v;
9190         if(this.rendered){
9191             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9192             this.validate();
9193         }
9194     },
9195     
9196     /*
9197     processValue : function(value){
9198         if(this.stripCharsRe){
9199             var newValue = value.replace(this.stripCharsRe, '');
9200             if(newValue !== value){
9201                 this.setRawValue(newValue);
9202                 return newValue;
9203             }
9204         }
9205         return value;
9206     },
9207   */
9208     preFocus : function(){
9209         
9210         if(this.selectOnFocus){
9211             this.inputEl().dom.select();
9212         }
9213     },
9214     filterKeys : function(e){
9215         var k = e.getKey();
9216         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9217             return;
9218         }
9219         var c = e.getCharCode(), cc = String.fromCharCode(c);
9220         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9221             return;
9222         }
9223         if(!this.maskRe.test(cc)){
9224             e.stopEvent();
9225         }
9226     },
9227      /**
9228      * Clear any invalid styles/messages for this field
9229      */
9230     clearInvalid : function(){
9231         
9232         if(!this.el || this.preventMark){ // not rendered
9233             return;
9234         }
9235         
9236      
9237         this.el.removeClass(this.invalidClass);
9238         
9239         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9240             
9241             var feedback = this.el.select('.form-control-feedback', true).first();
9242             
9243             if(feedback){
9244                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9245             }
9246             
9247         }
9248         
9249         this.fireEvent('valid', this);
9250     },
9251     
9252      /**
9253      * Mark this field as valid
9254      */
9255     markValid : function()
9256     {
9257         if(!this.el  || this.preventMark){ // not rendered...
9258             return;
9259         }
9260         
9261         this.el.removeClass([this.invalidClass, this.validClass]);
9262         
9263         var feedback = this.el.select('.form-control-feedback', true).first();
9264             
9265         if(feedback){
9266             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9267         }
9268         
9269         if(this.indicator){
9270             this.indicator.removeClass('visible');
9271             this.indicator.addClass('invisible');
9272         }
9273         
9274         if(this.disabled){
9275             return;
9276         }
9277         
9278         if(this.allowBlank && !this.getRawValue().length){
9279             return;
9280         }
9281         
9282         this.el.addClass(this.validClass);
9283         
9284         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9285             
9286             var feedback = this.el.select('.form-control-feedback', true).first();
9287             
9288             if(feedback){
9289                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9290                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9291             }
9292             
9293         }
9294         
9295         this.fireEvent('valid', this);
9296     },
9297     
9298      /**
9299      * Mark this field as invalid
9300      * @param {String} msg The validation message
9301      */
9302     markInvalid : function(msg)
9303     {
9304         if(!this.el  || this.preventMark){ // not rendered
9305             return;
9306         }
9307         
9308         this.el.removeClass([this.invalidClass, this.validClass]);
9309         
9310         var feedback = this.el.select('.form-control-feedback', true).first();
9311             
9312         if(feedback){
9313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9314         }
9315
9316         if(this.disabled){
9317             return;
9318         }
9319         
9320         if(this.allowBlank && !this.getRawValue().length){
9321             return;
9322         }
9323         
9324         if(this.indicator){
9325             this.indicator.removeClass('invisible');
9326             this.indicator.addClass('visible');
9327         }
9328         
9329         this.el.addClass(this.invalidClass);
9330         
9331         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9332             
9333             var feedback = this.el.select('.form-control-feedback', true).first();
9334             
9335             if(feedback){
9336                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9337                 
9338                 if(this.getValue().length || this.forceFeedback){
9339                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9340                 }
9341                 
9342             }
9343             
9344         }
9345         
9346         this.fireEvent('invalid', this, msg);
9347     },
9348     // private
9349     SafariOnKeyDown : function(event)
9350     {
9351         // this is a workaround for a password hang bug on chrome/ webkit.
9352         if (this.inputEl().dom.type != 'password') {
9353             return;
9354         }
9355         
9356         var isSelectAll = false;
9357         
9358         if(this.inputEl().dom.selectionEnd > 0){
9359             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9360         }
9361         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9362             event.preventDefault();
9363             this.setValue('');
9364             return;
9365         }
9366         
9367         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9368             
9369             event.preventDefault();
9370             // this is very hacky as keydown always get's upper case.
9371             //
9372             var cc = String.fromCharCode(event.getCharCode());
9373             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9374             
9375         }
9376     },
9377     adjustWidth : function(tag, w){
9378         tag = tag.toLowerCase();
9379         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9380             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9381                 if(tag == 'input'){
9382                     return w + 2;
9383                 }
9384                 if(tag == 'textarea'){
9385                     return w-2;
9386                 }
9387             }else if(Roo.isOpera){
9388                 if(tag == 'input'){
9389                     return w + 2;
9390                 }
9391                 if(tag == 'textarea'){
9392                     return w-2;
9393                 }
9394             }
9395         }
9396         return w;
9397     },
9398     
9399     setFieldLabel : function(v)
9400     {
9401         if(!this.rendered){
9402             return;
9403         }
9404         
9405         if(this.indicator){
9406             var ar = this.el.select('label > span',true);
9407             
9408             if (ar.elements.length) {
9409                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9410                 this.fieldLabel = v;
9411                 return;
9412             }
9413             
9414             var br = this.el.select('label',true);
9415             
9416             if(br.elements.length) {
9417                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9418                 this.fieldLabel = v;
9419                 return;
9420             }
9421             
9422             Roo.log('Cannot Found any of label > span || label in input');
9423             return;
9424         }
9425         
9426         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9427         this.fieldLabel = v;
9428         
9429         
9430     }
9431 });
9432
9433  
9434 /*
9435  * - LGPL
9436  *
9437  * Input
9438  * 
9439  */
9440
9441 /**
9442  * @class Roo.bootstrap.TextArea
9443  * @extends Roo.bootstrap.Input
9444  * Bootstrap TextArea class
9445  * @cfg {Number} cols Specifies the visible width of a text area
9446  * @cfg {Number} rows Specifies the visible number of lines in a text area
9447  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9448  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9449  * @cfg {string} html text
9450  * 
9451  * @constructor
9452  * Create a new TextArea
9453  * @param {Object} config The config object
9454  */
9455
9456 Roo.bootstrap.TextArea = function(config){
9457     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9458    
9459 };
9460
9461 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9462      
9463     cols : false,
9464     rows : 5,
9465     readOnly : false,
9466     warp : 'soft',
9467     resize : false,
9468     value: false,
9469     html: false,
9470     
9471     getAutoCreate : function(){
9472         
9473         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9474         
9475         var id = Roo.id();
9476         
9477         var cfg = {};
9478         
9479         if(this.inputType != 'hidden'){
9480             cfg.cls = 'form-group' //input-group
9481         }
9482         
9483         var input =  {
9484             tag: 'textarea',
9485             id : id,
9486             warp : this.warp,
9487             rows : this.rows,
9488             value : this.value || '',
9489             html: this.html || '',
9490             cls : 'form-control',
9491             placeholder : this.placeholder || '' 
9492             
9493         };
9494         
9495         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9496             input.maxLength = this.maxLength;
9497         }
9498         
9499         if(this.resize){
9500             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9501         }
9502         
9503         if(this.cols){
9504             input.cols = this.cols;
9505         }
9506         
9507         if (this.readOnly) {
9508             input.readonly = true;
9509         }
9510         
9511         if (this.name) {
9512             input.name = this.name;
9513         }
9514         
9515         if (this.size) {
9516             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9517         }
9518         
9519         var settings=this;
9520         ['xs','sm','md','lg'].map(function(size){
9521             if (settings[size]) {
9522                 cfg.cls += ' col-' + size + '-' + settings[size];
9523             }
9524         });
9525         
9526         var inputblock = input;
9527         
9528         if(this.hasFeedback && !this.allowBlank){
9529             
9530             var feedback = {
9531                 tag: 'span',
9532                 cls: 'glyphicon form-control-feedback'
9533             };
9534
9535             inputblock = {
9536                 cls : 'has-feedback',
9537                 cn :  [
9538                     input,
9539                     feedback
9540                 ] 
9541             };  
9542         }
9543         
9544         
9545         if (this.before || this.after) {
9546             
9547             inputblock = {
9548                 cls : 'input-group',
9549                 cn :  [] 
9550             };
9551             if (this.before) {
9552                 inputblock.cn.push({
9553                     tag :'span',
9554                     cls : 'input-group-addon',
9555                     html : this.before
9556                 });
9557             }
9558             
9559             inputblock.cn.push(input);
9560             
9561             if(this.hasFeedback && !this.allowBlank){
9562                 inputblock.cls += ' has-feedback';
9563                 inputblock.cn.push(feedback);
9564             }
9565             
9566             if (this.after) {
9567                 inputblock.cn.push({
9568                     tag :'span',
9569                     cls : 'input-group-addon',
9570                     html : this.after
9571                 });
9572             }
9573             
9574         }
9575         
9576         if (align ==='left' && this.fieldLabel.length) {
9577             cfg.cn = [
9578                 {
9579                     tag: 'label',
9580                     'for' :  id,
9581                     cls : 'control-label',
9582                     html : this.fieldLabel
9583                 },
9584                 {
9585                     cls : "",
9586                     cn: [
9587                         inputblock
9588                     ]
9589                 }
9590
9591             ];
9592             
9593             if(this.labelWidth > 12){
9594                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9595             }
9596
9597             if(this.labelWidth < 13 && this.labelmd == 0){
9598                 this.labelmd = this.labelWidth;
9599             }
9600
9601             if(this.labellg > 0){
9602                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9603                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9604             }
9605
9606             if(this.labelmd > 0){
9607                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9608                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9609             }
9610
9611             if(this.labelsm > 0){
9612                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9613                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9614             }
9615
9616             if(this.labelxs > 0){
9617                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9618                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9619             }
9620             
9621         } else if ( this.fieldLabel.length) {
9622             cfg.cn = [
9623
9624                {
9625                    tag: 'label',
9626                    //cls : 'input-group-addon',
9627                    html : this.fieldLabel
9628
9629                },
9630
9631                inputblock
9632
9633            ];
9634
9635         } else {
9636
9637             cfg.cn = [
9638
9639                 inputblock
9640
9641             ];
9642                 
9643         }
9644         
9645         if (this.disabled) {
9646             input.disabled=true;
9647         }
9648         
9649         return cfg;
9650         
9651     },
9652     /**
9653      * return the real textarea element.
9654      */
9655     inputEl: function ()
9656     {
9657         return this.el.select('textarea.form-control',true).first();
9658     },
9659     
9660     /**
9661      * Clear any invalid styles/messages for this field
9662      */
9663     clearInvalid : function()
9664     {
9665         
9666         if(!this.el || this.preventMark){ // not rendered
9667             return;
9668         }
9669         
9670         var label = this.el.select('label', true).first();
9671         var icon = this.el.select('i.fa-star', true).first();
9672         
9673         if(label && icon){
9674             icon.remove();
9675         }
9676         
9677         this.el.removeClass(this.invalidClass);
9678         
9679         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9680             
9681             var feedback = this.el.select('.form-control-feedback', true).first();
9682             
9683             if(feedback){
9684                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9685             }
9686             
9687         }
9688         
9689         this.fireEvent('valid', this);
9690     },
9691     
9692      /**
9693      * Mark this field as valid
9694      */
9695     markValid : function()
9696     {
9697         if(!this.el  || this.preventMark){ // not rendered
9698             return;
9699         }
9700         
9701         this.el.removeClass([this.invalidClass, this.validClass]);
9702         
9703         var feedback = this.el.select('.form-control-feedback', true).first();
9704             
9705         if(feedback){
9706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9707         }
9708
9709         if(this.disabled || this.allowBlank){
9710             return;
9711         }
9712         
9713         var label = this.el.select('label', true).first();
9714         var icon = this.el.select('i.fa-star', true).first();
9715         
9716         if(label && icon){
9717             icon.remove();
9718         }
9719         
9720         this.el.addClass(this.validClass);
9721         
9722         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9723             
9724             var feedback = this.el.select('.form-control-feedback', true).first();
9725             
9726             if(feedback){
9727                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9728                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9729             }
9730             
9731         }
9732         
9733         this.fireEvent('valid', this);
9734     },
9735     
9736      /**
9737      * Mark this field as invalid
9738      * @param {String} msg The validation message
9739      */
9740     markInvalid : function(msg)
9741     {
9742         if(!this.el  || this.preventMark){ // not rendered
9743             return;
9744         }
9745         
9746         this.el.removeClass([this.invalidClass, this.validClass]);
9747         
9748         var feedback = this.el.select('.form-control-feedback', true).first();
9749             
9750         if(feedback){
9751             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9752         }
9753
9754         if(this.disabled || this.allowBlank){
9755             return;
9756         }
9757         
9758         var label = this.el.select('label', true).first();
9759         var icon = this.el.select('i.fa-star', true).first();
9760         
9761         if(!this.getValue().length && label && !icon){
9762             this.el.createChild({
9763                 tag : 'i',
9764                 cls : 'text-danger fa fa-lg fa-star',
9765                 tooltip : 'This field is required',
9766                 style : 'margin-right:5px;'
9767             }, label, true);
9768         }
9769
9770         this.el.addClass(this.invalidClass);
9771         
9772         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9773             
9774             var feedback = this.el.select('.form-control-feedback', true).first();
9775             
9776             if(feedback){
9777                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9778                 
9779                 if(this.getValue().length || this.forceFeedback){
9780                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9781                 }
9782                 
9783             }
9784             
9785         }
9786         
9787         this.fireEvent('invalid', this, msg);
9788     }
9789 });
9790
9791  
9792 /*
9793  * - LGPL
9794  *
9795  * trigger field - base class for combo..
9796  * 
9797  */
9798  
9799 /**
9800  * @class Roo.bootstrap.TriggerField
9801  * @extends Roo.bootstrap.Input
9802  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9803  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9804  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9805  * for which you can provide a custom implementation.  For example:
9806  * <pre><code>
9807 var trigger = new Roo.bootstrap.TriggerField();
9808 trigger.onTriggerClick = myTriggerFn;
9809 trigger.applyTo('my-field');
9810 </code></pre>
9811  *
9812  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9813  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9814  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9815  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9816  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9817
9818  * @constructor
9819  * Create a new TriggerField.
9820  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9821  * to the base TextField)
9822  */
9823 Roo.bootstrap.TriggerField = function(config){
9824     this.mimicing = false;
9825     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9826 };
9827
9828 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9829     /**
9830      * @cfg {String} triggerClass A CSS class to apply to the trigger
9831      */
9832      /**
9833      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9834      */
9835     hideTrigger:false,
9836
9837     /**
9838      * @cfg {Boolean} removable (true|false) special filter default false
9839      */
9840     removable : false,
9841     
9842     /** @cfg {Boolean} grow @hide */
9843     /** @cfg {Number} growMin @hide */
9844     /** @cfg {Number} growMax @hide */
9845
9846     /**
9847      * @hide 
9848      * @method
9849      */
9850     autoSize: Roo.emptyFn,
9851     // private
9852     monitorTab : true,
9853     // private
9854     deferHeight : true,
9855
9856     
9857     actionMode : 'wrap',
9858     
9859     caret : false,
9860     
9861     
9862     getAutoCreate : function(){
9863        
9864         var align = this.labelAlign || this.parentLabelAlign();
9865         
9866         var id = Roo.id();
9867         
9868         var cfg = {
9869             cls: 'form-group' //input-group
9870         };
9871         
9872         
9873         var input =  {
9874             tag: 'input',
9875             id : id,
9876             type : this.inputType,
9877             cls : 'form-control',
9878             autocomplete: 'new-password',
9879             placeholder : this.placeholder || '' 
9880             
9881         };
9882         if (this.name) {
9883             input.name = this.name;
9884         }
9885         if (this.size) {
9886             input.cls += ' input-' + this.size;
9887         }
9888         
9889         if (this.disabled) {
9890             input.disabled=true;
9891         }
9892         
9893         var inputblock = input;
9894         
9895         if(this.hasFeedback && !this.allowBlank){
9896             
9897             var feedback = {
9898                 tag: 'span',
9899                 cls: 'glyphicon form-control-feedback'
9900             };
9901             
9902             if(this.removable && !this.editable && !this.tickable){
9903                 inputblock = {
9904                     cls : 'has-feedback',
9905                     cn :  [
9906                         inputblock,
9907                         {
9908                             tag: 'button',
9909                             html : 'x',
9910                             cls : 'roo-combo-removable-btn close'
9911                         },
9912                         feedback
9913                     ] 
9914                 };
9915             } else {
9916                 inputblock = {
9917                     cls : 'has-feedback',
9918                     cn :  [
9919                         inputblock,
9920                         feedback
9921                     ] 
9922                 };
9923             }
9924
9925         } else {
9926             if(this.removable && !this.editable && !this.tickable){
9927                 inputblock = {
9928                     cls : 'roo-removable',
9929                     cn :  [
9930                         inputblock,
9931                         {
9932                             tag: 'button',
9933                             html : 'x',
9934                             cls : 'roo-combo-removable-btn close'
9935                         }
9936                     ] 
9937                 };
9938             }
9939         }
9940         
9941         if (this.before || this.after) {
9942             
9943             inputblock = {
9944                 cls : 'input-group',
9945                 cn :  [] 
9946             };
9947             if (this.before) {
9948                 inputblock.cn.push({
9949                     tag :'span',
9950                     cls : 'input-group-addon',
9951                     html : this.before
9952                 });
9953             }
9954             
9955             inputblock.cn.push(input);
9956             
9957             if(this.hasFeedback && !this.allowBlank){
9958                 inputblock.cls += ' has-feedback';
9959                 inputblock.cn.push(feedback);
9960             }
9961             
9962             if (this.after) {
9963                 inputblock.cn.push({
9964                     tag :'span',
9965                     cls : 'input-group-addon',
9966                     html : this.after
9967                 });
9968             }
9969             
9970         };
9971         
9972         var box = {
9973             tag: 'div',
9974             cn: [
9975                 {
9976                     tag: 'input',
9977                     type : 'hidden',
9978                     cls: 'form-hidden-field'
9979                 },
9980                 inputblock
9981             ]
9982             
9983         };
9984         
9985         if(this.multiple){
9986             box = {
9987                 tag: 'div',
9988                 cn: [
9989                     {
9990                         tag: 'input',
9991                         type : 'hidden',
9992                         cls: 'form-hidden-field'
9993                     },
9994                     {
9995                         tag: 'ul',
9996                         cls: 'roo-select2-choices',
9997                         cn:[
9998                             {
9999                                 tag: 'li',
10000                                 cls: 'roo-select2-search-field',
10001                                 cn: [
10002
10003                                     inputblock
10004                                 ]
10005                             }
10006                         ]
10007                     }
10008                 ]
10009             }
10010         };
10011         
10012         var combobox = {
10013             cls: 'roo-select2-container input-group',
10014             cn: [
10015                 box
10016 //                {
10017 //                    tag: 'ul',
10018 //                    cls: 'typeahead typeahead-long dropdown-menu',
10019 //                    style: 'display:none'
10020 //                }
10021             ]
10022         };
10023         
10024         if(!this.multiple && this.showToggleBtn){
10025             
10026             var caret = {
10027                         tag: 'span',
10028                         cls: 'caret'
10029              };
10030             if (this.caret != false) {
10031                 caret = {
10032                      tag: 'i',
10033                      cls: 'fa fa-' + this.caret
10034                 };
10035                 
10036             }
10037             
10038             combobox.cn.push({
10039                 tag :'span',
10040                 cls : 'input-group-addon btn dropdown-toggle',
10041                 cn : [
10042                     caret,
10043                     {
10044                         tag: 'span',
10045                         cls: 'combobox-clear',
10046                         cn  : [
10047                             {
10048                                 tag : 'i',
10049                                 cls: 'icon-remove'
10050                             }
10051                         ]
10052                     }
10053                 ]
10054
10055             })
10056         }
10057         
10058         if(this.multiple){
10059             combobox.cls += ' roo-select2-container-multi';
10060         }
10061         
10062         if (align ==='left' && this.fieldLabel.length) {
10063             
10064             cfg.cls += ' roo-form-group-label-left';
10065
10066             cfg.cn = [
10067                 {
10068                     tag : 'i',
10069                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10070                     tooltip : 'This field is required'
10071                 },
10072                 {
10073                     tag: 'label',
10074                     'for' :  id,
10075                     cls : 'control-label',
10076                     html : this.fieldLabel
10077
10078                 },
10079                 {
10080                     cls : "", 
10081                     cn: [
10082                         combobox
10083                     ]
10084                 }
10085
10086             ];
10087             
10088             var labelCfg = cfg.cn[1];
10089             var contentCfg = cfg.cn[2];
10090             
10091             if(this.indicatorpos == 'right'){
10092                 cfg.cn = [
10093                     {
10094                         tag: 'label',
10095                         'for' :  id,
10096                         cls : 'control-label',
10097                         cn : [
10098                             {
10099                                 tag : 'span',
10100                                 html : this.fieldLabel
10101                             },
10102                             {
10103                                 tag : 'i',
10104                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10105                                 tooltip : 'This field is required'
10106                             }
10107                         ]
10108                     },
10109                     {
10110                         cls : "", 
10111                         cn: [
10112                             combobox
10113                         ]
10114                     }
10115
10116                 ];
10117                 
10118                 labelCfg = cfg.cn[0];
10119                 contentCfg = cfg.cn[1];
10120             }
10121             
10122             if(this.labelWidth > 12){
10123                 labelCfg.style = "width: " + this.labelWidth + 'px';
10124             }
10125             
10126             if(this.labelWidth < 13 && this.labelmd == 0){
10127                 this.labelmd = this.labelWidth;
10128             }
10129             
10130             if(this.labellg > 0){
10131                 labelCfg.cls += ' col-lg-' + this.labellg;
10132                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10133             }
10134             
10135             if(this.labelmd > 0){
10136                 labelCfg.cls += ' col-md-' + this.labelmd;
10137                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10138             }
10139             
10140             if(this.labelsm > 0){
10141                 labelCfg.cls += ' col-sm-' + this.labelsm;
10142                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10143             }
10144             
10145             if(this.labelxs > 0){
10146                 labelCfg.cls += ' col-xs-' + this.labelxs;
10147                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10148             }
10149             
10150         } else if ( this.fieldLabel.length) {
10151 //                Roo.log(" label");
10152             cfg.cn = [
10153                 {
10154                    tag : 'i',
10155                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10156                    tooltip : 'This field is required'
10157                },
10158                {
10159                    tag: 'label',
10160                    //cls : 'input-group-addon',
10161                    html : this.fieldLabel
10162
10163                },
10164
10165                combobox
10166
10167             ];
10168             
10169             if(this.indicatorpos == 'right'){
10170                 
10171                 cfg.cn = [
10172                     {
10173                        tag: 'label',
10174                        cn : [
10175                            {
10176                                tag : 'span',
10177                                html : this.fieldLabel
10178                            },
10179                            {
10180                               tag : 'i',
10181                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10182                               tooltip : 'This field is required'
10183                            }
10184                        ]
10185
10186                     },
10187                     combobox
10188
10189                 ];
10190
10191             }
10192
10193         } else {
10194             
10195 //                Roo.log(" no label && no align");
10196                 cfg = combobox
10197                      
10198                 
10199         }
10200         
10201         var settings=this;
10202         ['xs','sm','md','lg'].map(function(size){
10203             if (settings[size]) {
10204                 cfg.cls += ' col-' + size + '-' + settings[size];
10205             }
10206         });
10207         
10208         return cfg;
10209         
10210     },
10211     
10212     
10213     
10214     // private
10215     onResize : function(w, h){
10216 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10217 //        if(typeof w == 'number'){
10218 //            var x = w - this.trigger.getWidth();
10219 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10220 //            this.trigger.setStyle('left', x+'px');
10221 //        }
10222     },
10223
10224     // private
10225     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10226
10227     // private
10228     getResizeEl : function(){
10229         return this.inputEl();
10230     },
10231
10232     // private
10233     getPositionEl : function(){
10234         return this.inputEl();
10235     },
10236
10237     // private
10238     alignErrorIcon : function(){
10239         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10240     },
10241
10242     // private
10243     initEvents : function(){
10244         
10245         this.createList();
10246         
10247         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10248         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10249         if(!this.multiple && this.showToggleBtn){
10250             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10251             if(this.hideTrigger){
10252                 this.trigger.setDisplayed(false);
10253             }
10254             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10255         }
10256         
10257         if(this.multiple){
10258             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10259         }
10260         
10261         if(this.removable && !this.editable && !this.tickable){
10262             var close = this.closeTriggerEl();
10263             
10264             if(close){
10265                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10266                 close.on('click', this.removeBtnClick, this, close);
10267             }
10268         }
10269         
10270         //this.trigger.addClassOnOver('x-form-trigger-over');
10271         //this.trigger.addClassOnClick('x-form-trigger-click');
10272         
10273         //if(!this.width){
10274         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10275         //}
10276     },
10277     
10278     closeTriggerEl : function()
10279     {
10280         var close = this.el.select('.roo-combo-removable-btn', true).first();
10281         return close ? close : false;
10282     },
10283     
10284     removeBtnClick : function(e, h, el)
10285     {
10286         e.preventDefault();
10287         
10288         if(this.fireEvent("remove", this) !== false){
10289             this.reset();
10290             this.fireEvent("afterremove", this)
10291         }
10292     },
10293     
10294     createList : function()
10295     {
10296         this.list = Roo.get(document.body).createChild({
10297             tag: 'ul',
10298             cls: 'typeahead typeahead-long dropdown-menu',
10299             style: 'display:none'
10300         });
10301         
10302         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10303         
10304     },
10305
10306     // private
10307     initTrigger : function(){
10308        
10309     },
10310
10311     // private
10312     onDestroy : function(){
10313         if(this.trigger){
10314             this.trigger.removeAllListeners();
10315           //  this.trigger.remove();
10316         }
10317         //if(this.wrap){
10318         //    this.wrap.remove();
10319         //}
10320         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10321     },
10322
10323     // private
10324     onFocus : function(){
10325         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10326         /*
10327         if(!this.mimicing){
10328             this.wrap.addClass('x-trigger-wrap-focus');
10329             this.mimicing = true;
10330             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10331             if(this.monitorTab){
10332                 this.el.on("keydown", this.checkTab, this);
10333             }
10334         }
10335         */
10336     },
10337
10338     // private
10339     checkTab : function(e){
10340         if(e.getKey() == e.TAB){
10341             this.triggerBlur();
10342         }
10343     },
10344
10345     // private
10346     onBlur : function(){
10347         // do nothing
10348     },
10349
10350     // private
10351     mimicBlur : function(e, t){
10352         /*
10353         if(!this.wrap.contains(t) && this.validateBlur()){
10354             this.triggerBlur();
10355         }
10356         */
10357     },
10358
10359     // private
10360     triggerBlur : function(){
10361         this.mimicing = false;
10362         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10363         if(this.monitorTab){
10364             this.el.un("keydown", this.checkTab, this);
10365         }
10366         //this.wrap.removeClass('x-trigger-wrap-focus');
10367         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10368     },
10369
10370     // private
10371     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10372     validateBlur : function(e, t){
10373         return true;
10374     },
10375
10376     // private
10377     onDisable : function(){
10378         this.inputEl().dom.disabled = true;
10379         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10380         //if(this.wrap){
10381         //    this.wrap.addClass('x-item-disabled');
10382         //}
10383     },
10384
10385     // private
10386     onEnable : function(){
10387         this.inputEl().dom.disabled = false;
10388         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10389         //if(this.wrap){
10390         //    this.el.removeClass('x-item-disabled');
10391         //}
10392     },
10393
10394     // private
10395     onShow : function(){
10396         var ae = this.getActionEl();
10397         
10398         if(ae){
10399             ae.dom.style.display = '';
10400             ae.dom.style.visibility = 'visible';
10401         }
10402     },
10403
10404     // private
10405     
10406     onHide : function(){
10407         var ae = this.getActionEl();
10408         ae.dom.style.display = 'none';
10409     },
10410
10411     /**
10412      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10413      * by an implementing function.
10414      * @method
10415      * @param {EventObject} e
10416      */
10417     onTriggerClick : Roo.emptyFn
10418 });
10419  /*
10420  * Based on:
10421  * Ext JS Library 1.1.1
10422  * Copyright(c) 2006-2007, Ext JS, LLC.
10423  *
10424  * Originally Released Under LGPL - original licence link has changed is not relivant.
10425  *
10426  * Fork - LGPL
10427  * <script type="text/javascript">
10428  */
10429
10430
10431 /**
10432  * @class Roo.data.SortTypes
10433  * @singleton
10434  * Defines the default sorting (casting?) comparison functions used when sorting data.
10435  */
10436 Roo.data.SortTypes = {
10437     /**
10438      * Default sort that does nothing
10439      * @param {Mixed} s The value being converted
10440      * @return {Mixed} The comparison value
10441      */
10442     none : function(s){
10443         return s;
10444     },
10445     
10446     /**
10447      * The regular expression used to strip tags
10448      * @type {RegExp}
10449      * @property
10450      */
10451     stripTagsRE : /<\/?[^>]+>/gi,
10452     
10453     /**
10454      * Strips all HTML tags to sort on text only
10455      * @param {Mixed} s The value being converted
10456      * @return {String} The comparison value
10457      */
10458     asText : function(s){
10459         return String(s).replace(this.stripTagsRE, "");
10460     },
10461     
10462     /**
10463      * Strips all HTML tags to sort on text only - Case insensitive
10464      * @param {Mixed} s The value being converted
10465      * @return {String} The comparison value
10466      */
10467     asUCText : function(s){
10468         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10469     },
10470     
10471     /**
10472      * Case insensitive string
10473      * @param {Mixed} s The value being converted
10474      * @return {String} The comparison value
10475      */
10476     asUCString : function(s) {
10477         return String(s).toUpperCase();
10478     },
10479     
10480     /**
10481      * Date sorting
10482      * @param {Mixed} s The value being converted
10483      * @return {Number} The comparison value
10484      */
10485     asDate : function(s) {
10486         if(!s){
10487             return 0;
10488         }
10489         if(s instanceof Date){
10490             return s.getTime();
10491         }
10492         return Date.parse(String(s));
10493     },
10494     
10495     /**
10496      * Float sorting
10497      * @param {Mixed} s The value being converted
10498      * @return {Float} The comparison value
10499      */
10500     asFloat : function(s) {
10501         var val = parseFloat(String(s).replace(/,/g, ""));
10502         if(isNaN(val)) {
10503             val = 0;
10504         }
10505         return val;
10506     },
10507     
10508     /**
10509      * Integer sorting
10510      * @param {Mixed} s The value being converted
10511      * @return {Number} The comparison value
10512      */
10513     asInt : function(s) {
10514         var val = parseInt(String(s).replace(/,/g, ""));
10515         if(isNaN(val)) {
10516             val = 0;
10517         }
10518         return val;
10519     }
10520 };/*
10521  * Based on:
10522  * Ext JS Library 1.1.1
10523  * Copyright(c) 2006-2007, Ext JS, LLC.
10524  *
10525  * Originally Released Under LGPL - original licence link has changed is not relivant.
10526  *
10527  * Fork - LGPL
10528  * <script type="text/javascript">
10529  */
10530
10531 /**
10532 * @class Roo.data.Record
10533  * Instances of this class encapsulate both record <em>definition</em> information, and record
10534  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10535  * to access Records cached in an {@link Roo.data.Store} object.<br>
10536  * <p>
10537  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10538  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10539  * objects.<br>
10540  * <p>
10541  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10542  * @constructor
10543  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10544  * {@link #create}. The parameters are the same.
10545  * @param {Array} data An associative Array of data values keyed by the field name.
10546  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10547  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10548  * not specified an integer id is generated.
10549  */
10550 Roo.data.Record = function(data, id){
10551     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10552     this.data = data;
10553 };
10554
10555 /**
10556  * Generate a constructor for a specific record layout.
10557  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10558  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10559  * Each field definition object may contain the following properties: <ul>
10560  * <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,
10561  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10562  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10563  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10564  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10565  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10566  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10567  * this may be omitted.</p></li>
10568  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10569  * <ul><li>auto (Default, implies no conversion)</li>
10570  * <li>string</li>
10571  * <li>int</li>
10572  * <li>float</li>
10573  * <li>boolean</li>
10574  * <li>date</li></ul></p></li>
10575  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10576  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10577  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10578  * by the Reader into an object that will be stored in the Record. It is passed the
10579  * following parameters:<ul>
10580  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10581  * </ul></p></li>
10582  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10583  * </ul>
10584  * <br>usage:<br><pre><code>
10585 var TopicRecord = Roo.data.Record.create(
10586     {name: 'title', mapping: 'topic_title'},
10587     {name: 'author', mapping: 'username'},
10588     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10589     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10590     {name: 'lastPoster', mapping: 'user2'},
10591     {name: 'excerpt', mapping: 'post_text'}
10592 );
10593
10594 var myNewRecord = new TopicRecord({
10595     title: 'Do my job please',
10596     author: 'noobie',
10597     totalPosts: 1,
10598     lastPost: new Date(),
10599     lastPoster: 'Animal',
10600     excerpt: 'No way dude!'
10601 });
10602 myStore.add(myNewRecord);
10603 </code></pre>
10604  * @method create
10605  * @static
10606  */
10607 Roo.data.Record.create = function(o){
10608     var f = function(){
10609         f.superclass.constructor.apply(this, arguments);
10610     };
10611     Roo.extend(f, Roo.data.Record);
10612     var p = f.prototype;
10613     p.fields = new Roo.util.MixedCollection(false, function(field){
10614         return field.name;
10615     });
10616     for(var i = 0, len = o.length; i < len; i++){
10617         p.fields.add(new Roo.data.Field(o[i]));
10618     }
10619     f.getField = function(name){
10620         return p.fields.get(name);  
10621     };
10622     return f;
10623 };
10624
10625 Roo.data.Record.AUTO_ID = 1000;
10626 Roo.data.Record.EDIT = 'edit';
10627 Roo.data.Record.REJECT = 'reject';
10628 Roo.data.Record.COMMIT = 'commit';
10629
10630 Roo.data.Record.prototype = {
10631     /**
10632      * Readonly flag - true if this record has been modified.
10633      * @type Boolean
10634      */
10635     dirty : false,
10636     editing : false,
10637     error: null,
10638     modified: null,
10639
10640     // private
10641     join : function(store){
10642         this.store = store;
10643     },
10644
10645     /**
10646      * Set the named field to the specified value.
10647      * @param {String} name The name of the field to set.
10648      * @param {Object} value The value to set the field to.
10649      */
10650     set : function(name, value){
10651         if(this.data[name] == value){
10652             return;
10653         }
10654         this.dirty = true;
10655         if(!this.modified){
10656             this.modified = {};
10657         }
10658         if(typeof this.modified[name] == 'undefined'){
10659             this.modified[name] = this.data[name];
10660         }
10661         this.data[name] = value;
10662         if(!this.editing && this.store){
10663             this.store.afterEdit(this);
10664         }       
10665     },
10666
10667     /**
10668      * Get the value of the named field.
10669      * @param {String} name The name of the field to get the value of.
10670      * @return {Object} The value of the field.
10671      */
10672     get : function(name){
10673         return this.data[name]; 
10674     },
10675
10676     // private
10677     beginEdit : function(){
10678         this.editing = true;
10679         this.modified = {}; 
10680     },
10681
10682     // private
10683     cancelEdit : function(){
10684         this.editing = false;
10685         delete this.modified;
10686     },
10687
10688     // private
10689     endEdit : function(){
10690         this.editing = false;
10691         if(this.dirty && this.store){
10692             this.store.afterEdit(this);
10693         }
10694     },
10695
10696     /**
10697      * Usually called by the {@link Roo.data.Store} which owns the Record.
10698      * Rejects all changes made to the Record since either creation, or the last commit operation.
10699      * Modified fields are reverted to their original values.
10700      * <p>
10701      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10702      * of reject operations.
10703      */
10704     reject : function(){
10705         var m = this.modified;
10706         for(var n in m){
10707             if(typeof m[n] != "function"){
10708                 this.data[n] = m[n];
10709             }
10710         }
10711         this.dirty = false;
10712         delete this.modified;
10713         this.editing = false;
10714         if(this.store){
10715             this.store.afterReject(this);
10716         }
10717     },
10718
10719     /**
10720      * Usually called by the {@link Roo.data.Store} which owns the Record.
10721      * Commits all changes made to the Record since either creation, or the last commit operation.
10722      * <p>
10723      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10724      * of commit operations.
10725      */
10726     commit : function(){
10727         this.dirty = false;
10728         delete this.modified;
10729         this.editing = false;
10730         if(this.store){
10731             this.store.afterCommit(this);
10732         }
10733     },
10734
10735     // private
10736     hasError : function(){
10737         return this.error != null;
10738     },
10739
10740     // private
10741     clearError : function(){
10742         this.error = null;
10743     },
10744
10745     /**
10746      * Creates a copy of this record.
10747      * @param {String} id (optional) A new record id if you don't want to use this record's id
10748      * @return {Record}
10749      */
10750     copy : function(newId) {
10751         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10752     }
10753 };/*
10754  * Based on:
10755  * Ext JS Library 1.1.1
10756  * Copyright(c) 2006-2007, Ext JS, LLC.
10757  *
10758  * Originally Released Under LGPL - original licence link has changed is not relivant.
10759  *
10760  * Fork - LGPL
10761  * <script type="text/javascript">
10762  */
10763
10764
10765
10766 /**
10767  * @class Roo.data.Store
10768  * @extends Roo.util.Observable
10769  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10770  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10771  * <p>
10772  * 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
10773  * has no knowledge of the format of the data returned by the Proxy.<br>
10774  * <p>
10775  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10776  * instances from the data object. These records are cached and made available through accessor functions.
10777  * @constructor
10778  * Creates a new Store.
10779  * @param {Object} config A config object containing the objects needed for the Store to access data,
10780  * and read the data into Records.
10781  */
10782 Roo.data.Store = function(config){
10783     this.data = new Roo.util.MixedCollection(false);
10784     this.data.getKey = function(o){
10785         return o.id;
10786     };
10787     this.baseParams = {};
10788     // private
10789     this.paramNames = {
10790         "start" : "start",
10791         "limit" : "limit",
10792         "sort" : "sort",
10793         "dir" : "dir",
10794         "multisort" : "_multisort"
10795     };
10796
10797     if(config && config.data){
10798         this.inlineData = config.data;
10799         delete config.data;
10800     }
10801
10802     Roo.apply(this, config);
10803     
10804     if(this.reader){ // reader passed
10805         this.reader = Roo.factory(this.reader, Roo.data);
10806         this.reader.xmodule = this.xmodule || false;
10807         if(!this.recordType){
10808             this.recordType = this.reader.recordType;
10809         }
10810         if(this.reader.onMetaChange){
10811             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10812         }
10813     }
10814
10815     if(this.recordType){
10816         this.fields = this.recordType.prototype.fields;
10817     }
10818     this.modified = [];
10819
10820     this.addEvents({
10821         /**
10822          * @event datachanged
10823          * Fires when the data cache has changed, and a widget which is using this Store
10824          * as a Record cache should refresh its view.
10825          * @param {Store} this
10826          */
10827         datachanged : true,
10828         /**
10829          * @event metachange
10830          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10831          * @param {Store} this
10832          * @param {Object} meta The JSON metadata
10833          */
10834         metachange : true,
10835         /**
10836          * @event add
10837          * Fires when Records have been added to the Store
10838          * @param {Store} this
10839          * @param {Roo.data.Record[]} records The array of Records added
10840          * @param {Number} index The index at which the record(s) were added
10841          */
10842         add : true,
10843         /**
10844          * @event remove
10845          * Fires when a Record has been removed from the Store
10846          * @param {Store} this
10847          * @param {Roo.data.Record} record The Record that was removed
10848          * @param {Number} index The index at which the record was removed
10849          */
10850         remove : true,
10851         /**
10852          * @event update
10853          * Fires when a Record has been updated
10854          * @param {Store} this
10855          * @param {Roo.data.Record} record The Record that was updated
10856          * @param {String} operation The update operation being performed.  Value may be one of:
10857          * <pre><code>
10858  Roo.data.Record.EDIT
10859  Roo.data.Record.REJECT
10860  Roo.data.Record.COMMIT
10861          * </code></pre>
10862          */
10863         update : true,
10864         /**
10865          * @event clear
10866          * Fires when the data cache has been cleared.
10867          * @param {Store} this
10868          */
10869         clear : true,
10870         /**
10871          * @event beforeload
10872          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10873          * the load action will be canceled.
10874          * @param {Store} this
10875          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10876          */
10877         beforeload : true,
10878         /**
10879          * @event beforeloadadd
10880          * Fires after a new set of Records has been loaded.
10881          * @param {Store} this
10882          * @param {Roo.data.Record[]} records The Records that were loaded
10883          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10884          */
10885         beforeloadadd : true,
10886         /**
10887          * @event load
10888          * Fires after a new set of Records has been loaded, before they are added to the store.
10889          * @param {Store} this
10890          * @param {Roo.data.Record[]} records The Records that were loaded
10891          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10892          * @params {Object} return from reader
10893          */
10894         load : true,
10895         /**
10896          * @event loadexception
10897          * Fires if an exception occurs in the Proxy during loading.
10898          * Called with the signature of the Proxy's "loadexception" event.
10899          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10900          * 
10901          * @param {Proxy} 
10902          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10903          * @param {Object} load options 
10904          * @param {Object} jsonData from your request (normally this contains the Exception)
10905          */
10906         loadexception : true
10907     });
10908     
10909     if(this.proxy){
10910         this.proxy = Roo.factory(this.proxy, Roo.data);
10911         this.proxy.xmodule = this.xmodule || false;
10912         this.relayEvents(this.proxy,  ["loadexception"]);
10913     }
10914     this.sortToggle = {};
10915     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10916
10917     Roo.data.Store.superclass.constructor.call(this);
10918
10919     if(this.inlineData){
10920         this.loadData(this.inlineData);
10921         delete this.inlineData;
10922     }
10923 };
10924
10925 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10926      /**
10927     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10928     * without a remote query - used by combo/forms at present.
10929     */
10930     
10931     /**
10932     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10933     */
10934     /**
10935     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10936     */
10937     /**
10938     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10939     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10940     */
10941     /**
10942     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10943     * on any HTTP request
10944     */
10945     /**
10946     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10947     */
10948     /**
10949     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10950     */
10951     multiSort: false,
10952     /**
10953     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10954     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10955     */
10956     remoteSort : false,
10957
10958     /**
10959     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10960      * loaded or when a record is removed. (defaults to false).
10961     */
10962     pruneModifiedRecords : false,
10963
10964     // private
10965     lastOptions : null,
10966
10967     /**
10968      * Add Records to the Store and fires the add event.
10969      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10970      */
10971     add : function(records){
10972         records = [].concat(records);
10973         for(var i = 0, len = records.length; i < len; i++){
10974             records[i].join(this);
10975         }
10976         var index = this.data.length;
10977         this.data.addAll(records);
10978         this.fireEvent("add", this, records, index);
10979     },
10980
10981     /**
10982      * Remove a Record from the Store and fires the remove event.
10983      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10984      */
10985     remove : function(record){
10986         var index = this.data.indexOf(record);
10987         this.data.removeAt(index);
10988         if(this.pruneModifiedRecords){
10989             this.modified.remove(record);
10990         }
10991         this.fireEvent("remove", this, record, index);
10992     },
10993
10994     /**
10995      * Remove all Records from the Store and fires the clear event.
10996      */
10997     removeAll : function(){
10998         this.data.clear();
10999         if(this.pruneModifiedRecords){
11000             this.modified = [];
11001         }
11002         this.fireEvent("clear", this);
11003     },
11004
11005     /**
11006      * Inserts Records to the Store at the given index and fires the add event.
11007      * @param {Number} index The start index at which to insert the passed Records.
11008      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11009      */
11010     insert : function(index, records){
11011         records = [].concat(records);
11012         for(var i = 0, len = records.length; i < len; i++){
11013             this.data.insert(index, records[i]);
11014             records[i].join(this);
11015         }
11016         this.fireEvent("add", this, records, index);
11017     },
11018
11019     /**
11020      * Get the index within the cache of the passed Record.
11021      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11022      * @return {Number} The index of the passed Record. Returns -1 if not found.
11023      */
11024     indexOf : function(record){
11025         return this.data.indexOf(record);
11026     },
11027
11028     /**
11029      * Get the index within the cache of the Record with the passed id.
11030      * @param {String} id The id of the Record to find.
11031      * @return {Number} The index of the Record. Returns -1 if not found.
11032      */
11033     indexOfId : function(id){
11034         return this.data.indexOfKey(id);
11035     },
11036
11037     /**
11038      * Get the Record with the specified id.
11039      * @param {String} id The id of the Record to find.
11040      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11041      */
11042     getById : function(id){
11043         return this.data.key(id);
11044     },
11045
11046     /**
11047      * Get the Record at the specified index.
11048      * @param {Number} index The index of the Record to find.
11049      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11050      */
11051     getAt : function(index){
11052         return this.data.itemAt(index);
11053     },
11054
11055     /**
11056      * Returns a range of Records between specified indices.
11057      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11058      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11059      * @return {Roo.data.Record[]} An array of Records
11060      */
11061     getRange : function(start, end){
11062         return this.data.getRange(start, end);
11063     },
11064
11065     // private
11066     storeOptions : function(o){
11067         o = Roo.apply({}, o);
11068         delete o.callback;
11069         delete o.scope;
11070         this.lastOptions = o;
11071     },
11072
11073     /**
11074      * Loads the Record cache from the configured Proxy using the configured Reader.
11075      * <p>
11076      * If using remote paging, then the first load call must specify the <em>start</em>
11077      * and <em>limit</em> properties in the options.params property to establish the initial
11078      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11079      * <p>
11080      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11081      * and this call will return before the new data has been loaded. Perform any post-processing
11082      * in a callback function, or in a "load" event handler.</strong>
11083      * <p>
11084      * @param {Object} options An object containing properties which control loading options:<ul>
11085      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11086      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11087      * passed the following arguments:<ul>
11088      * <li>r : Roo.data.Record[]</li>
11089      * <li>options: Options object from the load call</li>
11090      * <li>success: Boolean success indicator</li></ul></li>
11091      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11092      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11093      * </ul>
11094      */
11095     load : function(options){
11096         options = options || {};
11097         if(this.fireEvent("beforeload", this, options) !== false){
11098             this.storeOptions(options);
11099             var p = Roo.apply(options.params || {}, this.baseParams);
11100             // if meta was not loaded from remote source.. try requesting it.
11101             if (!this.reader.metaFromRemote) {
11102                 p._requestMeta = 1;
11103             }
11104             if(this.sortInfo && this.remoteSort){
11105                 var pn = this.paramNames;
11106                 p[pn["sort"]] = this.sortInfo.field;
11107                 p[pn["dir"]] = this.sortInfo.direction;
11108             }
11109             if (this.multiSort) {
11110                 var pn = this.paramNames;
11111                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11112             }
11113             
11114             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11115         }
11116     },
11117
11118     /**
11119      * Reloads the Record cache from the configured Proxy using the configured Reader and
11120      * the options from the last load operation performed.
11121      * @param {Object} options (optional) An object containing properties which may override the options
11122      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11123      * the most recently used options are reused).
11124      */
11125     reload : function(options){
11126         this.load(Roo.applyIf(options||{}, this.lastOptions));
11127     },
11128
11129     // private
11130     // Called as a callback by the Reader during a load operation.
11131     loadRecords : function(o, options, success){
11132         if(!o || success === false){
11133             if(success !== false){
11134                 this.fireEvent("load", this, [], options, o);
11135             }
11136             if(options.callback){
11137                 options.callback.call(options.scope || this, [], options, false);
11138             }
11139             return;
11140         }
11141         // if data returned failure - throw an exception.
11142         if (o.success === false) {
11143             // show a message if no listener is registered.
11144             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11145                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11146             }
11147             // loadmask wil be hooked into this..
11148             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11149             return;
11150         }
11151         var r = o.records, t = o.totalRecords || r.length;
11152         
11153         this.fireEvent("beforeloadadd", this, r, options, o);
11154         
11155         if(!options || options.add !== true){
11156             if(this.pruneModifiedRecords){
11157                 this.modified = [];
11158             }
11159             for(var i = 0, len = r.length; i < len; i++){
11160                 r[i].join(this);
11161             }
11162             if(this.snapshot){
11163                 this.data = this.snapshot;
11164                 delete this.snapshot;
11165             }
11166             this.data.clear();
11167             this.data.addAll(r);
11168             this.totalLength = t;
11169             this.applySort();
11170             this.fireEvent("datachanged", this);
11171         }else{
11172             this.totalLength = Math.max(t, this.data.length+r.length);
11173             this.add(r);
11174         }
11175         
11176         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11177                 
11178             var e = new Roo.data.Record({});
11179
11180             e.set(this.parent.displayField, this.parent.emptyTitle);
11181             e.set(this.parent.valueField, '');
11182
11183             this.insert(0, e);
11184         }
11185             
11186         this.fireEvent("load", this, r, options, o);
11187         if(options.callback){
11188             options.callback.call(options.scope || this, r, options, true);
11189         }
11190     },
11191
11192
11193     /**
11194      * Loads data from a passed data block. A Reader which understands the format of the data
11195      * must have been configured in the constructor.
11196      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11197      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11198      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11199      */
11200     loadData : function(o, append){
11201         var r = this.reader.readRecords(o);
11202         this.loadRecords(r, {add: append}, true);
11203     },
11204
11205     /**
11206      * Gets the number of cached records.
11207      * <p>
11208      * <em>If using paging, this may not be the total size of the dataset. If the data object
11209      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11210      * the data set size</em>
11211      */
11212     getCount : function(){
11213         return this.data.length || 0;
11214     },
11215
11216     /**
11217      * Gets the total number of records in the dataset as returned by the server.
11218      * <p>
11219      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11220      * the dataset size</em>
11221      */
11222     getTotalCount : function(){
11223         return this.totalLength || 0;
11224     },
11225
11226     /**
11227      * Returns the sort state of the Store as an object with two properties:
11228      * <pre><code>
11229  field {String} The name of the field by which the Records are sorted
11230  direction {String} The sort order, "ASC" or "DESC"
11231      * </code></pre>
11232      */
11233     getSortState : function(){
11234         return this.sortInfo;
11235     },
11236
11237     // private
11238     applySort : function(){
11239         if(this.sortInfo && !this.remoteSort){
11240             var s = this.sortInfo, f = s.field;
11241             var st = this.fields.get(f).sortType;
11242             var fn = function(r1, r2){
11243                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11244                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11245             };
11246             this.data.sort(s.direction, fn);
11247             if(this.snapshot && this.snapshot != this.data){
11248                 this.snapshot.sort(s.direction, fn);
11249             }
11250         }
11251     },
11252
11253     /**
11254      * Sets the default sort column and order to be used by the next load operation.
11255      * @param {String} fieldName The name of the field to sort by.
11256      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11257      */
11258     setDefaultSort : function(field, dir){
11259         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11260     },
11261
11262     /**
11263      * Sort the Records.
11264      * If remote sorting is used, the sort is performed on the server, and the cache is
11265      * reloaded. If local sorting is used, the cache is sorted internally.
11266      * @param {String} fieldName The name of the field to sort by.
11267      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11268      */
11269     sort : function(fieldName, dir){
11270         var f = this.fields.get(fieldName);
11271         if(!dir){
11272             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11273             
11274             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11275                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11276             }else{
11277                 dir = f.sortDir;
11278             }
11279         }
11280         this.sortToggle[f.name] = dir;
11281         this.sortInfo = {field: f.name, direction: dir};
11282         if(!this.remoteSort){
11283             this.applySort();
11284             this.fireEvent("datachanged", this);
11285         }else{
11286             this.load(this.lastOptions);
11287         }
11288     },
11289
11290     /**
11291      * Calls the specified function for each of the Records in the cache.
11292      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11293      * Returning <em>false</em> aborts and exits the iteration.
11294      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11295      */
11296     each : function(fn, scope){
11297         this.data.each(fn, scope);
11298     },
11299
11300     /**
11301      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11302      * (e.g., during paging).
11303      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11304      */
11305     getModifiedRecords : function(){
11306         return this.modified;
11307     },
11308
11309     // private
11310     createFilterFn : function(property, value, anyMatch){
11311         if(!value.exec){ // not a regex
11312             value = String(value);
11313             if(value.length == 0){
11314                 return false;
11315             }
11316             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11317         }
11318         return function(r){
11319             return value.test(r.data[property]);
11320         };
11321     },
11322
11323     /**
11324      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11325      * @param {String} property A field on your records
11326      * @param {Number} start The record index to start at (defaults to 0)
11327      * @param {Number} end The last record index to include (defaults to length - 1)
11328      * @return {Number} The sum
11329      */
11330     sum : function(property, start, end){
11331         var rs = this.data.items, v = 0;
11332         start = start || 0;
11333         end = (end || end === 0) ? end : rs.length-1;
11334
11335         for(var i = start; i <= end; i++){
11336             v += (rs[i].data[property] || 0);
11337         }
11338         return v;
11339     },
11340
11341     /**
11342      * Filter the records by a specified property.
11343      * @param {String} field A field on your records
11344      * @param {String/RegExp} value Either a string that the field
11345      * should start with or a RegExp to test against the field
11346      * @param {Boolean} anyMatch True to match any part not just the beginning
11347      */
11348     filter : function(property, value, anyMatch){
11349         var fn = this.createFilterFn(property, value, anyMatch);
11350         return fn ? this.filterBy(fn) : this.clearFilter();
11351     },
11352
11353     /**
11354      * Filter by a function. The specified function will be called with each
11355      * record in this data source. If the function returns true the record is included,
11356      * otherwise it is filtered.
11357      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11358      * @param {Object} scope (optional) The scope of the function (defaults to this)
11359      */
11360     filterBy : function(fn, scope){
11361         this.snapshot = this.snapshot || this.data;
11362         this.data = this.queryBy(fn, scope||this);
11363         this.fireEvent("datachanged", this);
11364     },
11365
11366     /**
11367      * Query the records by a specified property.
11368      * @param {String} field A field on your records
11369      * @param {String/RegExp} value Either a string that the field
11370      * should start with or a RegExp to test against the field
11371      * @param {Boolean} anyMatch True to match any part not just the beginning
11372      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11373      */
11374     query : function(property, value, anyMatch){
11375         var fn = this.createFilterFn(property, value, anyMatch);
11376         return fn ? this.queryBy(fn) : this.data.clone();
11377     },
11378
11379     /**
11380      * Query by a function. The specified function will be called with each
11381      * record in this data source. If the function returns true the record is included
11382      * in the results.
11383      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11384      * @param {Object} scope (optional) The scope of the function (defaults to this)
11385       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11386      **/
11387     queryBy : function(fn, scope){
11388         var data = this.snapshot || this.data;
11389         return data.filterBy(fn, scope||this);
11390     },
11391
11392     /**
11393      * Collects unique values for a particular dataIndex from this store.
11394      * @param {String} dataIndex The property to collect
11395      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11396      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11397      * @return {Array} An array of the unique values
11398      **/
11399     collect : function(dataIndex, allowNull, bypassFilter){
11400         var d = (bypassFilter === true && this.snapshot) ?
11401                 this.snapshot.items : this.data.items;
11402         var v, sv, r = [], l = {};
11403         for(var i = 0, len = d.length; i < len; i++){
11404             v = d[i].data[dataIndex];
11405             sv = String(v);
11406             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11407                 l[sv] = true;
11408                 r[r.length] = v;
11409             }
11410         }
11411         return r;
11412     },
11413
11414     /**
11415      * Revert to a view of the Record cache with no filtering applied.
11416      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11417      */
11418     clearFilter : function(suppressEvent){
11419         if(this.snapshot && this.snapshot != this.data){
11420             this.data = this.snapshot;
11421             delete this.snapshot;
11422             if(suppressEvent !== true){
11423                 this.fireEvent("datachanged", this);
11424             }
11425         }
11426     },
11427
11428     // private
11429     afterEdit : function(record){
11430         if(this.modified.indexOf(record) == -1){
11431             this.modified.push(record);
11432         }
11433         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11434     },
11435     
11436     // private
11437     afterReject : function(record){
11438         this.modified.remove(record);
11439         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11440     },
11441
11442     // private
11443     afterCommit : function(record){
11444         this.modified.remove(record);
11445         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11446     },
11447
11448     /**
11449      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11450      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11451      */
11452     commitChanges : function(){
11453         var m = this.modified.slice(0);
11454         this.modified = [];
11455         for(var i = 0, len = m.length; i < len; i++){
11456             m[i].commit();
11457         }
11458     },
11459
11460     /**
11461      * Cancel outstanding changes on all changed records.
11462      */
11463     rejectChanges : function(){
11464         var m = this.modified.slice(0);
11465         this.modified = [];
11466         for(var i = 0, len = m.length; i < len; i++){
11467             m[i].reject();
11468         }
11469     },
11470
11471     onMetaChange : function(meta, rtype, o){
11472         this.recordType = rtype;
11473         this.fields = rtype.prototype.fields;
11474         delete this.snapshot;
11475         this.sortInfo = meta.sortInfo || this.sortInfo;
11476         this.modified = [];
11477         this.fireEvent('metachange', this, this.reader.meta);
11478     },
11479     
11480     moveIndex : function(data, type)
11481     {
11482         var index = this.indexOf(data);
11483         
11484         var newIndex = index + type;
11485         
11486         this.remove(data);
11487         
11488         this.insert(newIndex, data);
11489         
11490     }
11491 });/*
11492  * Based on:
11493  * Ext JS Library 1.1.1
11494  * Copyright(c) 2006-2007, Ext JS, LLC.
11495  *
11496  * Originally Released Under LGPL - original licence link has changed is not relivant.
11497  *
11498  * Fork - LGPL
11499  * <script type="text/javascript">
11500  */
11501
11502 /**
11503  * @class Roo.data.SimpleStore
11504  * @extends Roo.data.Store
11505  * Small helper class to make creating Stores from Array data easier.
11506  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11507  * @cfg {Array} fields An array of field definition objects, or field name strings.
11508  * @cfg {Array} data The multi-dimensional array of data
11509  * @constructor
11510  * @param {Object} config
11511  */
11512 Roo.data.SimpleStore = function(config){
11513     Roo.data.SimpleStore.superclass.constructor.call(this, {
11514         isLocal : true,
11515         reader: new Roo.data.ArrayReader({
11516                 id: config.id
11517             },
11518             Roo.data.Record.create(config.fields)
11519         ),
11520         proxy : new Roo.data.MemoryProxy(config.data)
11521     });
11522     this.load();
11523 };
11524 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11525  * Based on:
11526  * Ext JS Library 1.1.1
11527  * Copyright(c) 2006-2007, Ext JS, LLC.
11528  *
11529  * Originally Released Under LGPL - original licence link has changed is not relivant.
11530  *
11531  * Fork - LGPL
11532  * <script type="text/javascript">
11533  */
11534
11535 /**
11536 /**
11537  * @extends Roo.data.Store
11538  * @class Roo.data.JsonStore
11539  * Small helper class to make creating Stores for JSON data easier. <br/>
11540 <pre><code>
11541 var store = new Roo.data.JsonStore({
11542     url: 'get-images.php',
11543     root: 'images',
11544     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11545 });
11546 </code></pre>
11547  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11548  * JsonReader and HttpProxy (unless inline data is provided).</b>
11549  * @cfg {Array} fields An array of field definition objects, or field name strings.
11550  * @constructor
11551  * @param {Object} config
11552  */
11553 Roo.data.JsonStore = function(c){
11554     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11555         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11556         reader: new Roo.data.JsonReader(c, c.fields)
11557     }));
11558 };
11559 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11560  * Based on:
11561  * Ext JS Library 1.1.1
11562  * Copyright(c) 2006-2007, Ext JS, LLC.
11563  *
11564  * Originally Released Under LGPL - original licence link has changed is not relivant.
11565  *
11566  * Fork - LGPL
11567  * <script type="text/javascript">
11568  */
11569
11570  
11571 Roo.data.Field = function(config){
11572     if(typeof config == "string"){
11573         config = {name: config};
11574     }
11575     Roo.apply(this, config);
11576     
11577     if(!this.type){
11578         this.type = "auto";
11579     }
11580     
11581     var st = Roo.data.SortTypes;
11582     // named sortTypes are supported, here we look them up
11583     if(typeof this.sortType == "string"){
11584         this.sortType = st[this.sortType];
11585     }
11586     
11587     // set default sortType for strings and dates
11588     if(!this.sortType){
11589         switch(this.type){
11590             case "string":
11591                 this.sortType = st.asUCString;
11592                 break;
11593             case "date":
11594                 this.sortType = st.asDate;
11595                 break;
11596             default:
11597                 this.sortType = st.none;
11598         }
11599     }
11600
11601     // define once
11602     var stripRe = /[\$,%]/g;
11603
11604     // prebuilt conversion function for this field, instead of
11605     // switching every time we're reading a value
11606     if(!this.convert){
11607         var cv, dateFormat = this.dateFormat;
11608         switch(this.type){
11609             case "":
11610             case "auto":
11611             case undefined:
11612                 cv = function(v){ return v; };
11613                 break;
11614             case "string":
11615                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11616                 break;
11617             case "int":
11618                 cv = function(v){
11619                     return v !== undefined && v !== null && v !== '' ?
11620                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11621                     };
11622                 break;
11623             case "float":
11624                 cv = function(v){
11625                     return v !== undefined && v !== null && v !== '' ?
11626                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11627                     };
11628                 break;
11629             case "bool":
11630             case "boolean":
11631                 cv = function(v){ return v === true || v === "true" || v == 1; };
11632                 break;
11633             case "date":
11634                 cv = function(v){
11635                     if(!v){
11636                         return '';
11637                     }
11638                     if(v instanceof Date){
11639                         return v;
11640                     }
11641                     if(dateFormat){
11642                         if(dateFormat == "timestamp"){
11643                             return new Date(v*1000);
11644                         }
11645                         return Date.parseDate(v, dateFormat);
11646                     }
11647                     var parsed = Date.parse(v);
11648                     return parsed ? new Date(parsed) : null;
11649                 };
11650              break;
11651             
11652         }
11653         this.convert = cv;
11654     }
11655 };
11656
11657 Roo.data.Field.prototype = {
11658     dateFormat: null,
11659     defaultValue: "",
11660     mapping: null,
11661     sortType : null,
11662     sortDir : "ASC"
11663 };/*
11664  * Based on:
11665  * Ext JS Library 1.1.1
11666  * Copyright(c) 2006-2007, Ext JS, LLC.
11667  *
11668  * Originally Released Under LGPL - original licence link has changed is not relivant.
11669  *
11670  * Fork - LGPL
11671  * <script type="text/javascript">
11672  */
11673  
11674 // Base class for reading structured data from a data source.  This class is intended to be
11675 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11676
11677 /**
11678  * @class Roo.data.DataReader
11679  * Base class for reading structured data from a data source.  This class is intended to be
11680  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11681  */
11682
11683 Roo.data.DataReader = function(meta, recordType){
11684     
11685     this.meta = meta;
11686     
11687     this.recordType = recordType instanceof Array ? 
11688         Roo.data.Record.create(recordType) : recordType;
11689 };
11690
11691 Roo.data.DataReader.prototype = {
11692      /**
11693      * Create an empty record
11694      * @param {Object} data (optional) - overlay some values
11695      * @return {Roo.data.Record} record created.
11696      */
11697     newRow :  function(d) {
11698         var da =  {};
11699         this.recordType.prototype.fields.each(function(c) {
11700             switch( c.type) {
11701                 case 'int' : da[c.name] = 0; break;
11702                 case 'date' : da[c.name] = new Date(); break;
11703                 case 'float' : da[c.name] = 0.0; break;
11704                 case 'boolean' : da[c.name] = false; break;
11705                 default : da[c.name] = ""; break;
11706             }
11707             
11708         });
11709         return new this.recordType(Roo.apply(da, d));
11710     }
11711     
11712 };/*
11713  * Based on:
11714  * Ext JS Library 1.1.1
11715  * Copyright(c) 2006-2007, Ext JS, LLC.
11716  *
11717  * Originally Released Under LGPL - original licence link has changed is not relivant.
11718  *
11719  * Fork - LGPL
11720  * <script type="text/javascript">
11721  */
11722
11723 /**
11724  * @class Roo.data.DataProxy
11725  * @extends Roo.data.Observable
11726  * This class is an abstract base class for implementations which provide retrieval of
11727  * unformatted data objects.<br>
11728  * <p>
11729  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11730  * (of the appropriate type which knows how to parse the data object) to provide a block of
11731  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11732  * <p>
11733  * Custom implementations must implement the load method as described in
11734  * {@link Roo.data.HttpProxy#load}.
11735  */
11736 Roo.data.DataProxy = function(){
11737     this.addEvents({
11738         /**
11739          * @event beforeload
11740          * Fires before a network request is made to retrieve a data object.
11741          * @param {Object} This DataProxy object.
11742          * @param {Object} params The params parameter to the load function.
11743          */
11744         beforeload : true,
11745         /**
11746          * @event load
11747          * Fires before the load method's callback is called.
11748          * @param {Object} This DataProxy object.
11749          * @param {Object} o The data object.
11750          * @param {Object} arg The callback argument object passed to the load function.
11751          */
11752         load : true,
11753         /**
11754          * @event loadexception
11755          * Fires if an Exception occurs during data retrieval.
11756          * @param {Object} This DataProxy object.
11757          * @param {Object} o The data object.
11758          * @param {Object} arg The callback argument object passed to the load function.
11759          * @param {Object} e The Exception.
11760          */
11761         loadexception : true
11762     });
11763     Roo.data.DataProxy.superclass.constructor.call(this);
11764 };
11765
11766 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11767
11768     /**
11769      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11770      */
11771 /*
11772  * Based on:
11773  * Ext JS Library 1.1.1
11774  * Copyright(c) 2006-2007, Ext JS, LLC.
11775  *
11776  * Originally Released Under LGPL - original licence link has changed is not relivant.
11777  *
11778  * Fork - LGPL
11779  * <script type="text/javascript">
11780  */
11781 /**
11782  * @class Roo.data.MemoryProxy
11783  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11784  * to the Reader when its load method is called.
11785  * @constructor
11786  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11787  */
11788 Roo.data.MemoryProxy = function(data){
11789     if (data.data) {
11790         data = data.data;
11791     }
11792     Roo.data.MemoryProxy.superclass.constructor.call(this);
11793     this.data = data;
11794 };
11795
11796 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11797     
11798     /**
11799      * Load data from the requested source (in this case an in-memory
11800      * data object passed to the constructor), read the data object into
11801      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11802      * process that block using the passed callback.
11803      * @param {Object} params This parameter is not used by the MemoryProxy class.
11804      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11805      * object into a block of Roo.data.Records.
11806      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11807      * The function must be passed <ul>
11808      * <li>The Record block object</li>
11809      * <li>The "arg" argument from the load function</li>
11810      * <li>A boolean success indicator</li>
11811      * </ul>
11812      * @param {Object} scope The scope in which to call the callback
11813      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11814      */
11815     load : function(params, reader, callback, scope, arg){
11816         params = params || {};
11817         var result;
11818         try {
11819             result = reader.readRecords(this.data);
11820         }catch(e){
11821             this.fireEvent("loadexception", this, arg, null, e);
11822             callback.call(scope, null, arg, false);
11823             return;
11824         }
11825         callback.call(scope, result, arg, true);
11826     },
11827     
11828     // private
11829     update : function(params, records){
11830         
11831     }
11832 });/*
11833  * Based on:
11834  * Ext JS Library 1.1.1
11835  * Copyright(c) 2006-2007, Ext JS, LLC.
11836  *
11837  * Originally Released Under LGPL - original licence link has changed is not relivant.
11838  *
11839  * Fork - LGPL
11840  * <script type="text/javascript">
11841  */
11842 /**
11843  * @class Roo.data.HttpProxy
11844  * @extends Roo.data.DataProxy
11845  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11846  * configured to reference a certain URL.<br><br>
11847  * <p>
11848  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11849  * from which the running page was served.<br><br>
11850  * <p>
11851  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11852  * <p>
11853  * Be aware that to enable the browser to parse an XML document, the server must set
11854  * the Content-Type header in the HTTP response to "text/xml".
11855  * @constructor
11856  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11857  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11858  * will be used to make the request.
11859  */
11860 Roo.data.HttpProxy = function(conn){
11861     Roo.data.HttpProxy.superclass.constructor.call(this);
11862     // is conn a conn config or a real conn?
11863     this.conn = conn;
11864     this.useAjax = !conn || !conn.events;
11865   
11866 };
11867
11868 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11869     // thse are take from connection...
11870     
11871     /**
11872      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11873      */
11874     /**
11875      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11876      * extra parameters to each request made by this object. (defaults to undefined)
11877      */
11878     /**
11879      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11880      *  to each request made by this object. (defaults to undefined)
11881      */
11882     /**
11883      * @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)
11884      */
11885     /**
11886      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11887      */
11888      /**
11889      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11890      * @type Boolean
11891      */
11892   
11893
11894     /**
11895      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11896      * @type Boolean
11897      */
11898     /**
11899      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11900      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11901      * a finer-grained basis than the DataProxy events.
11902      */
11903     getConnection : function(){
11904         return this.useAjax ? Roo.Ajax : this.conn;
11905     },
11906
11907     /**
11908      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11909      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11910      * process that block using the passed callback.
11911      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11912      * for the request to the remote server.
11913      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11914      * object into a block of Roo.data.Records.
11915      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11916      * The function must be passed <ul>
11917      * <li>The Record block object</li>
11918      * <li>The "arg" argument from the load function</li>
11919      * <li>A boolean success indicator</li>
11920      * </ul>
11921      * @param {Object} scope The scope in which to call the callback
11922      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11923      */
11924     load : function(params, reader, callback, scope, arg){
11925         if(this.fireEvent("beforeload", this, params) !== false){
11926             var  o = {
11927                 params : params || {},
11928                 request: {
11929                     callback : callback,
11930                     scope : scope,
11931                     arg : arg
11932                 },
11933                 reader: reader,
11934                 callback : this.loadResponse,
11935                 scope: this
11936             };
11937             if(this.useAjax){
11938                 Roo.applyIf(o, this.conn);
11939                 if(this.activeRequest){
11940                     Roo.Ajax.abort(this.activeRequest);
11941                 }
11942                 this.activeRequest = Roo.Ajax.request(o);
11943             }else{
11944                 this.conn.request(o);
11945             }
11946         }else{
11947             callback.call(scope||this, null, arg, false);
11948         }
11949     },
11950
11951     // private
11952     loadResponse : function(o, success, response){
11953         delete this.activeRequest;
11954         if(!success){
11955             this.fireEvent("loadexception", this, o, response);
11956             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11957             return;
11958         }
11959         var result;
11960         try {
11961             result = o.reader.read(response);
11962         }catch(e){
11963             this.fireEvent("loadexception", this, o, response, e);
11964             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11965             return;
11966         }
11967         
11968         this.fireEvent("load", this, o, o.request.arg);
11969         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11970     },
11971
11972     // private
11973     update : function(dataSet){
11974
11975     },
11976
11977     // private
11978     updateResponse : function(dataSet){
11979
11980     }
11981 });/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991
11992 /**
11993  * @class Roo.data.ScriptTagProxy
11994  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11995  * other than the originating domain of the running page.<br><br>
11996  * <p>
11997  * <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
11998  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11999  * <p>
12000  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12001  * source code that is used as the source inside a &lt;script> tag.<br><br>
12002  * <p>
12003  * In order for the browser to process the returned data, the server must wrap the data object
12004  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12005  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12006  * depending on whether the callback name was passed:
12007  * <p>
12008  * <pre><code>
12009 boolean scriptTag = false;
12010 String cb = request.getParameter("callback");
12011 if (cb != null) {
12012     scriptTag = true;
12013     response.setContentType("text/javascript");
12014 } else {
12015     response.setContentType("application/x-json");
12016 }
12017 Writer out = response.getWriter();
12018 if (scriptTag) {
12019     out.write(cb + "(");
12020 }
12021 out.print(dataBlock.toJsonString());
12022 if (scriptTag) {
12023     out.write(");");
12024 }
12025 </pre></code>
12026  *
12027  * @constructor
12028  * @param {Object} config A configuration object.
12029  */
12030 Roo.data.ScriptTagProxy = function(config){
12031     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12032     Roo.apply(this, config);
12033     this.head = document.getElementsByTagName("head")[0];
12034 };
12035
12036 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12037
12038 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12039     /**
12040      * @cfg {String} url The URL from which to request the data object.
12041      */
12042     /**
12043      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12044      */
12045     timeout : 30000,
12046     /**
12047      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12048      * the server the name of the callback function set up by the load call to process the returned data object.
12049      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12050      * javascript output which calls this named function passing the data object as its only parameter.
12051      */
12052     callbackParam : "callback",
12053     /**
12054      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12055      * name to the request.
12056      */
12057     nocache : true,
12058
12059     /**
12060      * Load data from the configured URL, read the data object into
12061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12062      * process that block using the passed callback.
12063      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12064      * for the request to the remote server.
12065      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12066      * object into a block of Roo.data.Records.
12067      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12068      * The function must be passed <ul>
12069      * <li>The Record block object</li>
12070      * <li>The "arg" argument from the load function</li>
12071      * <li>A boolean success indicator</li>
12072      * </ul>
12073      * @param {Object} scope The scope in which to call the callback
12074      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075      */
12076     load : function(params, reader, callback, scope, arg){
12077         if(this.fireEvent("beforeload", this, params) !== false){
12078
12079             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12080
12081             var url = this.url;
12082             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12083             if(this.nocache){
12084                 url += "&_dc=" + (new Date().getTime());
12085             }
12086             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12087             var trans = {
12088                 id : transId,
12089                 cb : "stcCallback"+transId,
12090                 scriptId : "stcScript"+transId,
12091                 params : params,
12092                 arg : arg,
12093                 url : url,
12094                 callback : callback,
12095                 scope : scope,
12096                 reader : reader
12097             };
12098             var conn = this;
12099
12100             window[trans.cb] = function(o){
12101                 conn.handleResponse(o, trans);
12102             };
12103
12104             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12105
12106             if(this.autoAbort !== false){
12107                 this.abort();
12108             }
12109
12110             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12111
12112             var script = document.createElement("script");
12113             script.setAttribute("src", url);
12114             script.setAttribute("type", "text/javascript");
12115             script.setAttribute("id", trans.scriptId);
12116             this.head.appendChild(script);
12117
12118             this.trans = trans;
12119         }else{
12120             callback.call(scope||this, null, arg, false);
12121         }
12122     },
12123
12124     // private
12125     isLoading : function(){
12126         return this.trans ? true : false;
12127     },
12128
12129     /**
12130      * Abort the current server request.
12131      */
12132     abort : function(){
12133         if(this.isLoading()){
12134             this.destroyTrans(this.trans);
12135         }
12136     },
12137
12138     // private
12139     destroyTrans : function(trans, isLoaded){
12140         this.head.removeChild(document.getElementById(trans.scriptId));
12141         clearTimeout(trans.timeoutId);
12142         if(isLoaded){
12143             window[trans.cb] = undefined;
12144             try{
12145                 delete window[trans.cb];
12146             }catch(e){}
12147         }else{
12148             // if hasn't been loaded, wait for load to remove it to prevent script error
12149             window[trans.cb] = function(){
12150                 window[trans.cb] = undefined;
12151                 try{
12152                     delete window[trans.cb];
12153                 }catch(e){}
12154             };
12155         }
12156     },
12157
12158     // private
12159     handleResponse : function(o, trans){
12160         this.trans = false;
12161         this.destroyTrans(trans, true);
12162         var result;
12163         try {
12164             result = trans.reader.readRecords(o);
12165         }catch(e){
12166             this.fireEvent("loadexception", this, o, trans.arg, e);
12167             trans.callback.call(trans.scope||window, null, trans.arg, false);
12168             return;
12169         }
12170         this.fireEvent("load", this, o, trans.arg);
12171         trans.callback.call(trans.scope||window, result, trans.arg, true);
12172     },
12173
12174     // private
12175     handleFailure : function(trans){
12176         this.trans = false;
12177         this.destroyTrans(trans, false);
12178         this.fireEvent("loadexception", this, null, trans.arg);
12179         trans.callback.call(trans.scope||window, null, trans.arg, false);
12180     }
12181 });/*
12182  * Based on:
12183  * Ext JS Library 1.1.1
12184  * Copyright(c) 2006-2007, Ext JS, LLC.
12185  *
12186  * Originally Released Under LGPL - original licence link has changed is not relivant.
12187  *
12188  * Fork - LGPL
12189  * <script type="text/javascript">
12190  */
12191
12192 /**
12193  * @class Roo.data.JsonReader
12194  * @extends Roo.data.DataReader
12195  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12196  * based on mappings in a provided Roo.data.Record constructor.
12197  * 
12198  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12199  * in the reply previously. 
12200  * 
12201  * <p>
12202  * Example code:
12203  * <pre><code>
12204 var RecordDef = Roo.data.Record.create([
12205     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12206     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12207 ]);
12208 var myReader = new Roo.data.JsonReader({
12209     totalProperty: "results",    // The property which contains the total dataset size (optional)
12210     root: "rows",                // The property which contains an Array of row objects
12211     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12212 }, RecordDef);
12213 </code></pre>
12214  * <p>
12215  * This would consume a JSON file like this:
12216  * <pre><code>
12217 { 'results': 2, 'rows': [
12218     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12219     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12220 }
12221 </code></pre>
12222  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12223  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12224  * paged from the remote server.
12225  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12226  * @cfg {String} root name of the property which contains the Array of row objects.
12227  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12228  * @cfg {Array} fields Array of field definition objects
12229  * @constructor
12230  * Create a new JsonReader
12231  * @param {Object} meta Metadata configuration options
12232  * @param {Object} recordType Either an Array of field definition objects,
12233  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12234  */
12235 Roo.data.JsonReader = function(meta, recordType){
12236     
12237     meta = meta || {};
12238     // set some defaults:
12239     Roo.applyIf(meta, {
12240         totalProperty: 'total',
12241         successProperty : 'success',
12242         root : 'data',
12243         id : 'id'
12244     });
12245     
12246     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12247 };
12248 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12249     
12250     /**
12251      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12252      * Used by Store query builder to append _requestMeta to params.
12253      * 
12254      */
12255     metaFromRemote : false,
12256     /**
12257      * This method is only used by a DataProxy which has retrieved data from a remote server.
12258      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12259      * @return {Object} data A data block which is used by an Roo.data.Store object as
12260      * a cache of Roo.data.Records.
12261      */
12262     read : function(response){
12263         var json = response.responseText;
12264        
12265         var o = /* eval:var:o */ eval("("+json+")");
12266         if(!o) {
12267             throw {message: "JsonReader.read: Json object not found"};
12268         }
12269         
12270         if(o.metaData){
12271             
12272             delete this.ef;
12273             this.metaFromRemote = true;
12274             this.meta = o.metaData;
12275             this.recordType = Roo.data.Record.create(o.metaData.fields);
12276             this.onMetaChange(this.meta, this.recordType, o);
12277         }
12278         return this.readRecords(o);
12279     },
12280
12281     // private function a store will implement
12282     onMetaChange : function(meta, recordType, o){
12283
12284     },
12285
12286     /**
12287          * @ignore
12288          */
12289     simpleAccess: function(obj, subsc) {
12290         return obj[subsc];
12291     },
12292
12293         /**
12294          * @ignore
12295          */
12296     getJsonAccessor: function(){
12297         var re = /[\[\.]/;
12298         return function(expr) {
12299             try {
12300                 return(re.test(expr))
12301                     ? new Function("obj", "return obj." + expr)
12302                     : function(obj){
12303                         return obj[expr];
12304                     };
12305             } catch(e){}
12306             return Roo.emptyFn;
12307         };
12308     }(),
12309
12310     /**
12311      * Create a data block containing Roo.data.Records from an XML document.
12312      * @param {Object} o An object which contains an Array of row objects in the property specified
12313      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12314      * which contains the total size of the dataset.
12315      * @return {Object} data A data block which is used by an Roo.data.Store object as
12316      * a cache of Roo.data.Records.
12317      */
12318     readRecords : function(o){
12319         /**
12320          * After any data loads, the raw JSON data is available for further custom processing.
12321          * @type Object
12322          */
12323         this.o = o;
12324         var s = this.meta, Record = this.recordType,
12325             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12326
12327 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12328         if (!this.ef) {
12329             if(s.totalProperty) {
12330                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12331                 }
12332                 if(s.successProperty) {
12333                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12334                 }
12335                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12336                 if (s.id) {
12337                         var g = this.getJsonAccessor(s.id);
12338                         this.getId = function(rec) {
12339                                 var r = g(rec);  
12340                                 return (r === undefined || r === "") ? null : r;
12341                         };
12342                 } else {
12343                         this.getId = function(){return null;};
12344                 }
12345             this.ef = [];
12346             for(var jj = 0; jj < fl; jj++){
12347                 f = fi[jj];
12348                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12349                 this.ef[jj] = this.getJsonAccessor(map);
12350             }
12351         }
12352
12353         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12354         if(s.totalProperty){
12355             var vt = parseInt(this.getTotal(o), 10);
12356             if(!isNaN(vt)){
12357                 totalRecords = vt;
12358             }
12359         }
12360         if(s.successProperty){
12361             var vs = this.getSuccess(o);
12362             if(vs === false || vs === 'false'){
12363                 success = false;
12364             }
12365         }
12366         var records = [];
12367         for(var i = 0; i < c; i++){
12368                 var n = root[i];
12369             var values = {};
12370             var id = this.getId(n);
12371             for(var j = 0; j < fl; j++){
12372                 f = fi[j];
12373             var v = this.ef[j](n);
12374             if (!f.convert) {
12375                 Roo.log('missing convert for ' + f.name);
12376                 Roo.log(f);
12377                 continue;
12378             }
12379             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12380             }
12381             var record = new Record(values, id);
12382             record.json = n;
12383             records[i] = record;
12384         }
12385         return {
12386             raw : o,
12387             success : success,
12388             records : records,
12389             totalRecords : totalRecords
12390         };
12391     }
12392 });/*
12393  * Based on:
12394  * Ext JS Library 1.1.1
12395  * Copyright(c) 2006-2007, Ext JS, LLC.
12396  *
12397  * Originally Released Under LGPL - original licence link has changed is not relivant.
12398  *
12399  * Fork - LGPL
12400  * <script type="text/javascript">
12401  */
12402
12403 /**
12404  * @class Roo.data.ArrayReader
12405  * @extends Roo.data.DataReader
12406  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12407  * Each element of that Array represents a row of data fields. The
12408  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12409  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12410  * <p>
12411  * Example code:.
12412  * <pre><code>
12413 var RecordDef = Roo.data.Record.create([
12414     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12415     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12416 ]);
12417 var myReader = new Roo.data.ArrayReader({
12418     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12419 }, RecordDef);
12420 </code></pre>
12421  * <p>
12422  * This would consume an Array like this:
12423  * <pre><code>
12424 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12425   </code></pre>
12426  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12427  * @constructor
12428  * Create a new JsonReader
12429  * @param {Object} meta Metadata configuration options.
12430  * @param {Object} recordType Either an Array of field definition objects
12431  * as specified to {@link Roo.data.Record#create},
12432  * or an {@link Roo.data.Record} object
12433  * created using {@link Roo.data.Record#create}.
12434  */
12435 Roo.data.ArrayReader = function(meta, recordType){
12436     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12437 };
12438
12439 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12440     /**
12441      * Create a data block containing Roo.data.Records from an XML document.
12442      * @param {Object} o An Array of row objects which represents the dataset.
12443      * @return {Object} data A data block which is used by an Roo.data.Store object as
12444      * a cache of Roo.data.Records.
12445      */
12446     readRecords : function(o){
12447         var sid = this.meta ? this.meta.id : null;
12448         var recordType = this.recordType, fields = recordType.prototype.fields;
12449         var records = [];
12450         var root = o;
12451             for(var i = 0; i < root.length; i++){
12452                     var n = root[i];
12453                 var values = {};
12454                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12455                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12456                 var f = fields.items[j];
12457                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12458                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12459                 v = f.convert(v);
12460                 values[f.name] = v;
12461             }
12462                 var record = new recordType(values, id);
12463                 record.json = n;
12464                 records[records.length] = record;
12465             }
12466             return {
12467                 records : records,
12468                 totalRecords : records.length
12469             };
12470     }
12471 });/*
12472  * - LGPL
12473  * * 
12474  */
12475
12476 /**
12477  * @class Roo.bootstrap.ComboBox
12478  * @extends Roo.bootstrap.TriggerField
12479  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12480  * @cfg {Boolean} append (true|false) default false
12481  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12482  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12483  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12484  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12485  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12486  * @cfg {Boolean} animate default true
12487  * @cfg {Boolean} emptyResultText only for touch device
12488  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12489  * @cfg {String} emptyTitle default ''
12490  * @constructor
12491  * Create a new ComboBox.
12492  * @param {Object} config Configuration options
12493  */
12494 Roo.bootstrap.ComboBox = function(config){
12495     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12496     this.addEvents({
12497         /**
12498          * @event expand
12499          * Fires when the dropdown list is expanded
12500         * @param {Roo.bootstrap.ComboBox} combo This combo box
12501         */
12502         'expand' : true,
12503         /**
12504          * @event collapse
12505          * Fires when the dropdown list is collapsed
12506         * @param {Roo.bootstrap.ComboBox} combo This combo box
12507         */
12508         'collapse' : true,
12509         /**
12510          * @event beforeselect
12511          * Fires before a list item is selected. Return false to cancel the selection.
12512         * @param {Roo.bootstrap.ComboBox} combo This combo box
12513         * @param {Roo.data.Record} record The data record returned from the underlying store
12514         * @param {Number} index The index of the selected item in the dropdown list
12515         */
12516         'beforeselect' : true,
12517         /**
12518          * @event select
12519          * Fires when a list item is selected
12520         * @param {Roo.bootstrap.ComboBox} combo This combo box
12521         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12522         * @param {Number} index The index of the selected item in the dropdown list
12523         */
12524         'select' : true,
12525         /**
12526          * @event beforequery
12527          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12528          * The event object passed has these properties:
12529         * @param {Roo.bootstrap.ComboBox} combo This combo box
12530         * @param {String} query The query
12531         * @param {Boolean} forceAll true to force "all" query
12532         * @param {Boolean} cancel true to cancel the query
12533         * @param {Object} e The query event object
12534         */
12535         'beforequery': true,
12536          /**
12537          * @event add
12538          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12539         * @param {Roo.bootstrap.ComboBox} combo This combo box
12540         */
12541         'add' : true,
12542         /**
12543          * @event edit
12544          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12545         * @param {Roo.bootstrap.ComboBox} combo This combo box
12546         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12547         */
12548         'edit' : true,
12549         /**
12550          * @event remove
12551          * Fires when the remove value from the combobox array
12552         * @param {Roo.bootstrap.ComboBox} combo This combo box
12553         */
12554         'remove' : true,
12555         /**
12556          * @event afterremove
12557          * Fires when the remove value from the combobox array
12558         * @param {Roo.bootstrap.ComboBox} combo This combo box
12559         */
12560         'afterremove' : true,
12561         /**
12562          * @event specialfilter
12563          * Fires when specialfilter
12564             * @param {Roo.bootstrap.ComboBox} combo This combo box
12565             */
12566         'specialfilter' : true,
12567         /**
12568          * @event tick
12569          * Fires when tick the element
12570             * @param {Roo.bootstrap.ComboBox} combo This combo box
12571             */
12572         'tick' : true,
12573         /**
12574          * @event touchviewdisplay
12575          * Fires when touch view require special display (default is using displayField)
12576             * @param {Roo.bootstrap.ComboBox} combo This combo box
12577             * @param {Object} cfg set html .
12578             */
12579         'touchviewdisplay' : true
12580         
12581     });
12582     
12583     this.item = [];
12584     this.tickItems = [];
12585     
12586     this.selectedIndex = -1;
12587     if(this.mode == 'local'){
12588         if(config.queryDelay === undefined){
12589             this.queryDelay = 10;
12590         }
12591         if(config.minChars === undefined){
12592             this.minChars = 0;
12593         }
12594     }
12595 };
12596
12597 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12598      
12599     /**
12600      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12601      * rendering into an Roo.Editor, defaults to false)
12602      */
12603     /**
12604      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12605      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12606      */
12607     /**
12608      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12609      */
12610     /**
12611      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12612      * the dropdown list (defaults to undefined, with no header element)
12613      */
12614
12615      /**
12616      * @cfg {String/Roo.Template} tpl The template to use to render the output
12617      */
12618      
12619      /**
12620      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12621      */
12622     listWidth: undefined,
12623     /**
12624      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12625      * mode = 'remote' or 'text' if mode = 'local')
12626      */
12627     displayField: undefined,
12628     
12629     /**
12630      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12631      * mode = 'remote' or 'value' if mode = 'local'). 
12632      * Note: use of a valueField requires the user make a selection
12633      * in order for a value to be mapped.
12634      */
12635     valueField: undefined,
12636     /**
12637      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12638      */
12639     modalTitle : '',
12640     
12641     /**
12642      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12643      * field's data value (defaults to the underlying DOM element's name)
12644      */
12645     hiddenName: undefined,
12646     /**
12647      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12648      */
12649     listClass: '',
12650     /**
12651      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12652      */
12653     selectedClass: 'active',
12654     
12655     /**
12656      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12657      */
12658     shadow:'sides',
12659     /**
12660      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12661      * anchor positions (defaults to 'tl-bl')
12662      */
12663     listAlign: 'tl-bl?',
12664     /**
12665      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12666      */
12667     maxHeight: 300,
12668     /**
12669      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12670      * query specified by the allQuery config option (defaults to 'query')
12671      */
12672     triggerAction: 'query',
12673     /**
12674      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12675      * (defaults to 4, does not apply if editable = false)
12676      */
12677     minChars : 4,
12678     /**
12679      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12680      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12681      */
12682     typeAhead: false,
12683     /**
12684      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12685      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12686      */
12687     queryDelay: 500,
12688     /**
12689      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12690      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12691      */
12692     pageSize: 0,
12693     /**
12694      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12695      * when editable = true (defaults to false)
12696      */
12697     selectOnFocus:false,
12698     /**
12699      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12700      */
12701     queryParam: 'query',
12702     /**
12703      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12704      * when mode = 'remote' (defaults to 'Loading...')
12705      */
12706     loadingText: 'Loading...',
12707     /**
12708      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12709      */
12710     resizable: false,
12711     /**
12712      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12713      */
12714     handleHeight : 8,
12715     /**
12716      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12717      * traditional select (defaults to true)
12718      */
12719     editable: true,
12720     /**
12721      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12722      */
12723     allQuery: '',
12724     /**
12725      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12726      */
12727     mode: 'remote',
12728     /**
12729      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12730      * listWidth has a higher value)
12731      */
12732     minListWidth : 70,
12733     /**
12734      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12735      * allow the user to set arbitrary text into the field (defaults to false)
12736      */
12737     forceSelection:false,
12738     /**
12739      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12740      * if typeAhead = true (defaults to 250)
12741      */
12742     typeAheadDelay : 250,
12743     /**
12744      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12745      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12746      */
12747     valueNotFoundText : undefined,
12748     /**
12749      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12750      */
12751     blockFocus : false,
12752     
12753     /**
12754      * @cfg {Boolean} disableClear Disable showing of clear button.
12755      */
12756     disableClear : false,
12757     /**
12758      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12759      */
12760     alwaysQuery : false,
12761     
12762     /**
12763      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12764      */
12765     multiple : false,
12766     
12767     /**
12768      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12769      */
12770     invalidClass : "has-warning",
12771     
12772     /**
12773      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12774      */
12775     validClass : "has-success",
12776     
12777     /**
12778      * @cfg {Boolean} specialFilter (true|false) special filter default false
12779      */
12780     specialFilter : false,
12781     
12782     /**
12783      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12784      */
12785     mobileTouchView : true,
12786     
12787     /**
12788      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12789      */
12790     useNativeIOS : false,
12791     
12792     ios_options : false,
12793     
12794     //private
12795     addicon : false,
12796     editicon: false,
12797     
12798     page: 0,
12799     hasQuery: false,
12800     append: false,
12801     loadNext: false,
12802     autoFocus : true,
12803     tickable : false,
12804     btnPosition : 'right',
12805     triggerList : true,
12806     showToggleBtn : true,
12807     animate : true,
12808     emptyResultText: 'Empty',
12809     triggerText : 'Select',
12810     emptyTitle : '',
12811     
12812     // element that contains real text value.. (when hidden is used..)
12813     
12814     getAutoCreate : function()
12815     {   
12816         var cfg = false;
12817         //render
12818         /*
12819          * Render classic select for iso
12820          */
12821         
12822         if(Roo.isIOS && this.useNativeIOS){
12823             cfg = this.getAutoCreateNativeIOS();
12824             return cfg;
12825         }
12826         
12827         /*
12828          * Touch Devices
12829          */
12830         
12831         if(Roo.isTouch && this.mobileTouchView){
12832             cfg = this.getAutoCreateTouchView();
12833             return cfg;;
12834         }
12835         
12836         /*
12837          *  Normal ComboBox
12838          */
12839         if(!this.tickable){
12840             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12841             return cfg;
12842         }
12843         
12844         /*
12845          *  ComboBox with tickable selections
12846          */
12847              
12848         var align = this.labelAlign || this.parentLabelAlign();
12849         
12850         cfg = {
12851             cls : 'form-group roo-combobox-tickable' //input-group
12852         };
12853         
12854         var btn_text_select = '';
12855         var btn_text_done = '';
12856         var btn_text_cancel = '';
12857         
12858         if (this.btn_text_show) {
12859             btn_text_select = 'Select';
12860             btn_text_done = 'Done';
12861             btn_text_cancel = 'Cancel'; 
12862         }
12863         
12864         var buttons = {
12865             tag : 'div',
12866             cls : 'tickable-buttons',
12867             cn : [
12868                 {
12869                     tag : 'button',
12870                     type : 'button',
12871                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12872                     //html : this.triggerText
12873                     html: btn_text_select
12874                 },
12875                 {
12876                     tag : 'button',
12877                     type : 'button',
12878                     name : 'ok',
12879                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12880                     //html : 'Done'
12881                     html: btn_text_done
12882                 },
12883                 {
12884                     tag : 'button',
12885                     type : 'button',
12886                     name : 'cancel',
12887                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12888                     //html : 'Cancel'
12889                     html: btn_text_cancel
12890                 }
12891             ]
12892         };
12893         
12894         if(this.editable){
12895             buttons.cn.unshift({
12896                 tag: 'input',
12897                 cls: 'roo-select2-search-field-input'
12898             });
12899         }
12900         
12901         var _this = this;
12902         
12903         Roo.each(buttons.cn, function(c){
12904             if (_this.size) {
12905                 c.cls += ' btn-' + _this.size;
12906             }
12907
12908             if (_this.disabled) {
12909                 c.disabled = true;
12910             }
12911         });
12912         
12913         var box = {
12914             tag: 'div',
12915             cn: [
12916                 {
12917                     tag: 'input',
12918                     type : 'hidden',
12919                     cls: 'form-hidden-field'
12920                 },
12921                 {
12922                     tag: 'ul',
12923                     cls: 'roo-select2-choices',
12924                     cn:[
12925                         {
12926                             tag: 'li',
12927                             cls: 'roo-select2-search-field',
12928                             cn: [
12929                                 buttons
12930                             ]
12931                         }
12932                     ]
12933                 }
12934             ]
12935         };
12936         
12937         var combobox = {
12938             cls: 'roo-select2-container input-group roo-select2-container-multi',
12939             cn: [
12940                 box
12941 //                {
12942 //                    tag: 'ul',
12943 //                    cls: 'typeahead typeahead-long dropdown-menu',
12944 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12945 //                }
12946             ]
12947         };
12948         
12949         if(this.hasFeedback && !this.allowBlank){
12950             
12951             var feedback = {
12952                 tag: 'span',
12953                 cls: 'glyphicon form-control-feedback'
12954             };
12955
12956             combobox.cn.push(feedback);
12957         }
12958         
12959         
12960         if (align ==='left' && this.fieldLabel.length) {
12961             
12962             cfg.cls += ' roo-form-group-label-left';
12963             
12964             cfg.cn = [
12965                 {
12966                     tag : 'i',
12967                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12968                     tooltip : 'This field is required'
12969                 },
12970                 {
12971                     tag: 'label',
12972                     'for' :  id,
12973                     cls : 'control-label',
12974                     html : this.fieldLabel
12975
12976                 },
12977                 {
12978                     cls : "", 
12979                     cn: [
12980                         combobox
12981                     ]
12982                 }
12983
12984             ];
12985             
12986             var labelCfg = cfg.cn[1];
12987             var contentCfg = cfg.cn[2];
12988             
12989
12990             if(this.indicatorpos == 'right'){
12991                 
12992                 cfg.cn = [
12993                     {
12994                         tag: 'label',
12995                         'for' :  id,
12996                         cls : 'control-label',
12997                         cn : [
12998                             {
12999                                 tag : 'span',
13000                                 html : this.fieldLabel
13001                             },
13002                             {
13003                                 tag : 'i',
13004                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13005                                 tooltip : 'This field is required'
13006                             }
13007                         ]
13008                     },
13009                     {
13010                         cls : "",
13011                         cn: [
13012                             combobox
13013                         ]
13014                     }
13015
13016                 ];
13017                 
13018                 
13019                 
13020                 labelCfg = cfg.cn[0];
13021                 contentCfg = cfg.cn[1];
13022             
13023             }
13024             
13025             if(this.labelWidth > 12){
13026                 labelCfg.style = "width: " + this.labelWidth + 'px';
13027             }
13028             
13029             if(this.labelWidth < 13 && this.labelmd == 0){
13030                 this.labelmd = this.labelWidth;
13031             }
13032             
13033             if(this.labellg > 0){
13034                 labelCfg.cls += ' col-lg-' + this.labellg;
13035                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13036             }
13037             
13038             if(this.labelmd > 0){
13039                 labelCfg.cls += ' col-md-' + this.labelmd;
13040                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13041             }
13042             
13043             if(this.labelsm > 0){
13044                 labelCfg.cls += ' col-sm-' + this.labelsm;
13045                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13046             }
13047             
13048             if(this.labelxs > 0){
13049                 labelCfg.cls += ' col-xs-' + this.labelxs;
13050                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13051             }
13052                 
13053                 
13054         } else if ( this.fieldLabel.length) {
13055 //                Roo.log(" label");
13056                  cfg.cn = [
13057                     {
13058                         tag : 'i',
13059                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13060                         tooltip : 'This field is required'
13061                     },
13062                     {
13063                         tag: 'label',
13064                         //cls : 'input-group-addon',
13065                         html : this.fieldLabel
13066                     },
13067                     combobox
13068                 ];
13069                 
13070                 if(this.indicatorpos == 'right'){
13071                     cfg.cn = [
13072                         {
13073                             tag: 'label',
13074                             //cls : 'input-group-addon',
13075                             html : this.fieldLabel
13076                         },
13077                         {
13078                             tag : 'i',
13079                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13080                             tooltip : 'This field is required'
13081                         },
13082                         combobox
13083                     ];
13084                     
13085                 }
13086
13087         } else {
13088             
13089 //                Roo.log(" no label && no align");
13090                 cfg = combobox
13091                      
13092                 
13093         }
13094          
13095         var settings=this;
13096         ['xs','sm','md','lg'].map(function(size){
13097             if (settings[size]) {
13098                 cfg.cls += ' col-' + size + '-' + settings[size];
13099             }
13100         });
13101         
13102         return cfg;
13103         
13104     },
13105     
13106     _initEventsCalled : false,
13107     
13108     // private
13109     initEvents: function()
13110     {   
13111         if (this._initEventsCalled) { // as we call render... prevent looping...
13112             return;
13113         }
13114         this._initEventsCalled = true;
13115         
13116         if (!this.store) {
13117             throw "can not find store for combo";
13118         }
13119         
13120         this.indicator = this.indicatorEl();
13121         
13122         this.store = Roo.factory(this.store, Roo.data);
13123         this.store.parent = this;
13124         
13125         // if we are building from html. then this element is so complex, that we can not really
13126         // use the rendered HTML.
13127         // so we have to trash and replace the previous code.
13128         if (Roo.XComponent.build_from_html) {
13129             // remove this element....
13130             var e = this.el.dom, k=0;
13131             while (e ) { e = e.previousSibling;  ++k;}
13132
13133             this.el.remove();
13134             
13135             this.el=false;
13136             this.rendered = false;
13137             
13138             this.render(this.parent().getChildContainer(true), k);
13139         }
13140         
13141         if(Roo.isIOS && this.useNativeIOS){
13142             this.initIOSView();
13143             return;
13144         }
13145         
13146         /*
13147          * Touch Devices
13148          */
13149         
13150         if(Roo.isTouch && this.mobileTouchView){
13151             this.initTouchView();
13152             return;
13153         }
13154         
13155         if(this.tickable){
13156             this.initTickableEvents();
13157             return;
13158         }
13159         
13160         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13161         
13162         if(this.hiddenName){
13163             
13164             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13165             
13166             this.hiddenField.dom.value =
13167                 this.hiddenValue !== undefined ? this.hiddenValue :
13168                 this.value !== undefined ? this.value : '';
13169
13170             // prevent input submission
13171             this.el.dom.removeAttribute('name');
13172             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13173              
13174              
13175         }
13176         //if(Roo.isGecko){
13177         //    this.el.dom.setAttribute('autocomplete', 'off');
13178         //}
13179         
13180         var cls = 'x-combo-list';
13181         
13182         //this.list = new Roo.Layer({
13183         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13184         //});
13185         
13186         var _this = this;
13187         
13188         (function(){
13189             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13190             _this.list.setWidth(lw);
13191         }).defer(100);
13192         
13193         this.list.on('mouseover', this.onViewOver, this);
13194         this.list.on('mousemove', this.onViewMove, this);
13195         this.list.on('scroll', this.onViewScroll, this);
13196         
13197         /*
13198         this.list.swallowEvent('mousewheel');
13199         this.assetHeight = 0;
13200
13201         if(this.title){
13202             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13203             this.assetHeight += this.header.getHeight();
13204         }
13205
13206         this.innerList = this.list.createChild({cls:cls+'-inner'});
13207         this.innerList.on('mouseover', this.onViewOver, this);
13208         this.innerList.on('mousemove', this.onViewMove, this);
13209         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13210         
13211         if(this.allowBlank && !this.pageSize && !this.disableClear){
13212             this.footer = this.list.createChild({cls:cls+'-ft'});
13213             this.pageTb = new Roo.Toolbar(this.footer);
13214            
13215         }
13216         if(this.pageSize){
13217             this.footer = this.list.createChild({cls:cls+'-ft'});
13218             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13219                     {pageSize: this.pageSize});
13220             
13221         }
13222         
13223         if (this.pageTb && this.allowBlank && !this.disableClear) {
13224             var _this = this;
13225             this.pageTb.add(new Roo.Toolbar.Fill(), {
13226                 cls: 'x-btn-icon x-btn-clear',
13227                 text: '&#160;',
13228                 handler: function()
13229                 {
13230                     _this.collapse();
13231                     _this.clearValue();
13232                     _this.onSelect(false, -1);
13233                 }
13234             });
13235         }
13236         if (this.footer) {
13237             this.assetHeight += this.footer.getHeight();
13238         }
13239         */
13240             
13241         if(!this.tpl){
13242             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13243         }
13244
13245         this.view = new Roo.View(this.list, this.tpl, {
13246             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13247         });
13248         //this.view.wrapEl.setDisplayed(false);
13249         this.view.on('click', this.onViewClick, this);
13250         
13251         
13252         this.store.on('beforeload', this.onBeforeLoad, this);
13253         this.store.on('load', this.onLoad, this);
13254         this.store.on('loadexception', this.onLoadException, this);
13255         /*
13256         if(this.resizable){
13257             this.resizer = new Roo.Resizable(this.list,  {
13258                pinned:true, handles:'se'
13259             });
13260             this.resizer.on('resize', function(r, w, h){
13261                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13262                 this.listWidth = w;
13263                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13264                 this.restrictHeight();
13265             }, this);
13266             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13267         }
13268         */
13269         if(!this.editable){
13270             this.editable = true;
13271             this.setEditable(false);
13272         }
13273         
13274         /*
13275         
13276         if (typeof(this.events.add.listeners) != 'undefined') {
13277             
13278             this.addicon = this.wrap.createChild(
13279                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13280        
13281             this.addicon.on('click', function(e) {
13282                 this.fireEvent('add', this);
13283             }, this);
13284         }
13285         if (typeof(this.events.edit.listeners) != 'undefined') {
13286             
13287             this.editicon = this.wrap.createChild(
13288                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13289             if (this.addicon) {
13290                 this.editicon.setStyle('margin-left', '40px');
13291             }
13292             this.editicon.on('click', function(e) {
13293                 
13294                 // we fire even  if inothing is selected..
13295                 this.fireEvent('edit', this, this.lastData );
13296                 
13297             }, this);
13298         }
13299         */
13300         
13301         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13302             "up" : function(e){
13303                 this.inKeyMode = true;
13304                 this.selectPrev();
13305             },
13306
13307             "down" : function(e){
13308                 if(!this.isExpanded()){
13309                     this.onTriggerClick();
13310                 }else{
13311                     this.inKeyMode = true;
13312                     this.selectNext();
13313                 }
13314             },
13315
13316             "enter" : function(e){
13317 //                this.onViewClick();
13318                 //return true;
13319                 this.collapse();
13320                 
13321                 if(this.fireEvent("specialkey", this, e)){
13322                     this.onViewClick(false);
13323                 }
13324                 
13325                 return true;
13326             },
13327
13328             "esc" : function(e){
13329                 this.collapse();
13330             },
13331
13332             "tab" : function(e){
13333                 this.collapse();
13334                 
13335                 if(this.fireEvent("specialkey", this, e)){
13336                     this.onViewClick(false);
13337                 }
13338                 
13339                 return true;
13340             },
13341
13342             scope : this,
13343
13344             doRelay : function(foo, bar, hname){
13345                 if(hname == 'down' || this.scope.isExpanded()){
13346                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13347                 }
13348                 return true;
13349             },
13350
13351             forceKeyDown: true
13352         });
13353         
13354         
13355         this.queryDelay = Math.max(this.queryDelay || 10,
13356                 this.mode == 'local' ? 10 : 250);
13357         
13358         
13359         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13360         
13361         if(this.typeAhead){
13362             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13363         }
13364         if(this.editable !== false){
13365             this.inputEl().on("keyup", this.onKeyUp, this);
13366         }
13367         if(this.forceSelection){
13368             this.inputEl().on('blur', this.doForce, this);
13369         }
13370         
13371         if(this.multiple){
13372             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13373             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13374         }
13375     },
13376     
13377     initTickableEvents: function()
13378     {   
13379         this.createList();
13380         
13381         if(this.hiddenName){
13382             
13383             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13384             
13385             this.hiddenField.dom.value =
13386                 this.hiddenValue !== undefined ? this.hiddenValue :
13387                 this.value !== undefined ? this.value : '';
13388
13389             // prevent input submission
13390             this.el.dom.removeAttribute('name');
13391             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13392              
13393              
13394         }
13395         
13396 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13397         
13398         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13399         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13400         if(this.triggerList){
13401             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13402         }
13403          
13404         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13405         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13406         
13407         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13408         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13409         
13410         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13411         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13412         
13413         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13414         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13415         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13416         
13417         this.okBtn.hide();
13418         this.cancelBtn.hide();
13419         
13420         var _this = this;
13421         
13422         (function(){
13423             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13424             _this.list.setWidth(lw);
13425         }).defer(100);
13426         
13427         this.list.on('mouseover', this.onViewOver, this);
13428         this.list.on('mousemove', this.onViewMove, this);
13429         
13430         this.list.on('scroll', this.onViewScroll, this);
13431         
13432         if(!this.tpl){
13433             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>';
13434         }
13435
13436         this.view = new Roo.View(this.list, this.tpl, {
13437             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13438         });
13439         
13440         //this.view.wrapEl.setDisplayed(false);
13441         this.view.on('click', this.onViewClick, this);
13442         
13443         
13444         
13445         this.store.on('beforeload', this.onBeforeLoad, this);
13446         this.store.on('load', this.onLoad, this);
13447         this.store.on('loadexception', this.onLoadException, this);
13448         
13449         if(this.editable){
13450             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13451                 "up" : function(e){
13452                     this.inKeyMode = true;
13453                     this.selectPrev();
13454                 },
13455
13456                 "down" : function(e){
13457                     this.inKeyMode = true;
13458                     this.selectNext();
13459                 },
13460
13461                 "enter" : function(e){
13462                     if(this.fireEvent("specialkey", this, e)){
13463                         this.onViewClick(false);
13464                     }
13465                     
13466                     return true;
13467                 },
13468
13469                 "esc" : function(e){
13470                     this.onTickableFooterButtonClick(e, false, false);
13471                 },
13472
13473                 "tab" : function(e){
13474                     this.fireEvent("specialkey", this, e);
13475                     
13476                     this.onTickableFooterButtonClick(e, false, false);
13477                     
13478                     return true;
13479                 },
13480
13481                 scope : this,
13482
13483                 doRelay : function(e, fn, key){
13484                     if(this.scope.isExpanded()){
13485                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13486                     }
13487                     return true;
13488                 },
13489
13490                 forceKeyDown: true
13491             });
13492         }
13493         
13494         this.queryDelay = Math.max(this.queryDelay || 10,
13495                 this.mode == 'local' ? 10 : 250);
13496         
13497         
13498         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13499         
13500         if(this.typeAhead){
13501             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13502         }
13503         
13504         if(this.editable !== false){
13505             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13506         }
13507         
13508         this.indicator = this.indicatorEl();
13509         
13510         if(this.indicator){
13511             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13512             this.indicator.hide();
13513         }
13514         
13515     },
13516
13517     onDestroy : function(){
13518         if(this.view){
13519             this.view.setStore(null);
13520             this.view.el.removeAllListeners();
13521             this.view.el.remove();
13522             this.view.purgeListeners();
13523         }
13524         if(this.list){
13525             this.list.dom.innerHTML  = '';
13526         }
13527         
13528         if(this.store){
13529             this.store.un('beforeload', this.onBeforeLoad, this);
13530             this.store.un('load', this.onLoad, this);
13531             this.store.un('loadexception', this.onLoadException, this);
13532         }
13533         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13534     },
13535
13536     // private
13537     fireKey : function(e){
13538         if(e.isNavKeyPress() && !this.list.isVisible()){
13539             this.fireEvent("specialkey", this, e);
13540         }
13541     },
13542
13543     // private
13544     onResize: function(w, h){
13545 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13546 //        
13547 //        if(typeof w != 'number'){
13548 //            // we do not handle it!?!?
13549 //            return;
13550 //        }
13551 //        var tw = this.trigger.getWidth();
13552 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13553 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13554 //        var x = w - tw;
13555 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13556 //            
13557 //        //this.trigger.setStyle('left', x+'px');
13558 //        
13559 //        if(this.list && this.listWidth === undefined){
13560 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13561 //            this.list.setWidth(lw);
13562 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13563 //        }
13564         
13565     
13566         
13567     },
13568
13569     /**
13570      * Allow or prevent the user from directly editing the field text.  If false is passed,
13571      * the user will only be able to select from the items defined in the dropdown list.  This method
13572      * is the runtime equivalent of setting the 'editable' config option at config time.
13573      * @param {Boolean} value True to allow the user to directly edit the field text
13574      */
13575     setEditable : function(value){
13576         if(value == this.editable){
13577             return;
13578         }
13579         this.editable = value;
13580         if(!value){
13581             this.inputEl().dom.setAttribute('readOnly', true);
13582             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13583             this.inputEl().addClass('x-combo-noedit');
13584         }else{
13585             this.inputEl().dom.setAttribute('readOnly', false);
13586             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13587             this.inputEl().removeClass('x-combo-noedit');
13588         }
13589     },
13590
13591     // private
13592     
13593     onBeforeLoad : function(combo,opts){
13594         if(!this.hasFocus){
13595             return;
13596         }
13597          if (!opts.add) {
13598             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13599          }
13600         this.restrictHeight();
13601         this.selectedIndex = -1;
13602     },
13603
13604     // private
13605     onLoad : function(){
13606         
13607         this.hasQuery = false;
13608         
13609         if(!this.hasFocus){
13610             return;
13611         }
13612         
13613         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13614             this.loading.hide();
13615         }
13616         
13617         if(this.store.getCount() > 0){
13618             
13619             this.expand();
13620             this.restrictHeight();
13621             if(this.lastQuery == this.allQuery){
13622                 if(this.editable && !this.tickable){
13623                     this.inputEl().dom.select();
13624                 }
13625                 
13626                 if(
13627                     !this.selectByValue(this.value, true) &&
13628                     this.autoFocus && 
13629                     (
13630                         !this.store.lastOptions ||
13631                         typeof(this.store.lastOptions.add) == 'undefined' || 
13632                         this.store.lastOptions.add != true
13633                     )
13634                 ){
13635                     this.select(0, true);
13636                 }
13637             }else{
13638                 if(this.autoFocus){
13639                     this.selectNext();
13640                 }
13641                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13642                     this.taTask.delay(this.typeAheadDelay);
13643                 }
13644             }
13645         }else{
13646             this.onEmptyResults();
13647         }
13648         
13649         //this.el.focus();
13650     },
13651     // private
13652     onLoadException : function()
13653     {
13654         this.hasQuery = false;
13655         
13656         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13657             this.loading.hide();
13658         }
13659         
13660         if(this.tickable && this.editable){
13661             return;
13662         }
13663         
13664         this.collapse();
13665         // only causes errors at present
13666         //Roo.log(this.store.reader.jsonData);
13667         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13668             // fixme
13669             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13670         //}
13671         
13672         
13673     },
13674     // private
13675     onTypeAhead : function(){
13676         if(this.store.getCount() > 0){
13677             var r = this.store.getAt(0);
13678             var newValue = r.data[this.displayField];
13679             var len = newValue.length;
13680             var selStart = this.getRawValue().length;
13681             
13682             if(selStart != len){
13683                 this.setRawValue(newValue);
13684                 this.selectText(selStart, newValue.length);
13685             }
13686         }
13687     },
13688
13689     // private
13690     onSelect : function(record, index){
13691         
13692         if(this.fireEvent('beforeselect', this, record, index) !== false){
13693         
13694             this.setFromData(index > -1 ? record.data : false);
13695             
13696             this.collapse();
13697             this.fireEvent('select', this, record, index);
13698         }
13699     },
13700
13701     /**
13702      * Returns the currently selected field value or empty string if no value is set.
13703      * @return {String} value The selected value
13704      */
13705     getValue : function()
13706     {
13707         if(Roo.isIOS && this.useNativeIOS){
13708             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13709         }
13710         
13711         if(this.multiple){
13712             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13713         }
13714         
13715         if(this.valueField){
13716             return typeof this.value != 'undefined' ? this.value : '';
13717         }else{
13718             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13719         }
13720     },
13721     
13722     getRawValue : function()
13723     {
13724         if(Roo.isIOS && this.useNativeIOS){
13725             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13726         }
13727         
13728         var v = this.inputEl().getValue();
13729         
13730         return v;
13731     },
13732
13733     /**
13734      * Clears any text/value currently set in the field
13735      */
13736     clearValue : function(){
13737         
13738         if(this.hiddenField){
13739             this.hiddenField.dom.value = '';
13740         }
13741         this.value = '';
13742         this.setRawValue('');
13743         this.lastSelectionText = '';
13744         this.lastData = false;
13745         
13746         var close = this.closeTriggerEl();
13747         
13748         if(close){
13749             close.hide();
13750         }
13751         
13752         this.validate();
13753         
13754     },
13755
13756     /**
13757      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13758      * will be displayed in the field.  If the value does not match the data value of an existing item,
13759      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13760      * Otherwise the field will be blank (although the value will still be set).
13761      * @param {String} value The value to match
13762      */
13763     setValue : function(v)
13764     {
13765         if(Roo.isIOS && this.useNativeIOS){
13766             this.setIOSValue(v);
13767             return;
13768         }
13769         
13770         if(this.multiple){
13771             this.syncValue();
13772             return;
13773         }
13774         
13775         var text = v;
13776         if(this.valueField){
13777             var r = this.findRecord(this.valueField, v);
13778             if(r){
13779                 text = r.data[this.displayField];
13780             }else if(this.valueNotFoundText !== undefined){
13781                 text = this.valueNotFoundText;
13782             }
13783         }
13784         this.lastSelectionText = text;
13785         if(this.hiddenField){
13786             this.hiddenField.dom.value = v;
13787         }
13788         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13789         this.value = v;
13790         
13791         var close = this.closeTriggerEl();
13792         
13793         if(close){
13794             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13795         }
13796         
13797         this.validate();
13798     },
13799     /**
13800      * @property {Object} the last set data for the element
13801      */
13802     
13803     lastData : false,
13804     /**
13805      * Sets the value of the field based on a object which is related to the record format for the store.
13806      * @param {Object} value the value to set as. or false on reset?
13807      */
13808     setFromData : function(o){
13809         
13810         if(this.multiple){
13811             this.addItem(o);
13812             return;
13813         }
13814             
13815         var dv = ''; // display value
13816         var vv = ''; // value value..
13817         this.lastData = o;
13818         if (this.displayField) {
13819             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13820         } else {
13821             // this is an error condition!!!
13822             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13823         }
13824         
13825         if(this.valueField){
13826             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13827         }
13828         
13829         var close = this.closeTriggerEl();
13830         
13831         if(close){
13832             if(dv.length || vv * 1 > 0){
13833                 close.show() ;
13834                 this.blockFocus=true;
13835             } else {
13836                 close.hide();
13837             }             
13838         }
13839         
13840         if(this.hiddenField){
13841             this.hiddenField.dom.value = vv;
13842             
13843             this.lastSelectionText = dv;
13844             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13845             this.value = vv;
13846             return;
13847         }
13848         // no hidden field.. - we store the value in 'value', but still display
13849         // display field!!!!
13850         this.lastSelectionText = dv;
13851         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13852         this.value = vv;
13853         
13854         
13855         
13856     },
13857     // private
13858     reset : function(){
13859         // overridden so that last data is reset..
13860         
13861         if(this.multiple){
13862             this.clearItem();
13863             return;
13864         }
13865         
13866         this.setValue(this.originalValue);
13867         //this.clearInvalid();
13868         this.lastData = false;
13869         if (this.view) {
13870             this.view.clearSelections();
13871         }
13872         
13873         this.validate();
13874     },
13875     // private
13876     findRecord : function(prop, value){
13877         var record;
13878         if(this.store.getCount() > 0){
13879             this.store.each(function(r){
13880                 if(r.data[prop] == value){
13881                     record = r;
13882                     return false;
13883                 }
13884                 return true;
13885             });
13886         }
13887         return record;
13888     },
13889     
13890     getName: function()
13891     {
13892         // returns hidden if it's set..
13893         if (!this.rendered) {return ''};
13894         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13895         
13896     },
13897     // private
13898     onViewMove : function(e, t){
13899         this.inKeyMode = false;
13900     },
13901
13902     // private
13903     onViewOver : function(e, t){
13904         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13905             return;
13906         }
13907         var item = this.view.findItemFromChild(t);
13908         
13909         if(item){
13910             var index = this.view.indexOf(item);
13911             this.select(index, false);
13912         }
13913     },
13914
13915     // private
13916     onViewClick : function(view, doFocus, el, e)
13917     {
13918         var index = this.view.getSelectedIndexes()[0];
13919         
13920         var r = this.store.getAt(index);
13921         
13922         if(this.tickable){
13923             
13924             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13925                 return;
13926             }
13927             
13928             var rm = false;
13929             var _this = this;
13930             
13931             Roo.each(this.tickItems, function(v,k){
13932                 
13933                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13934                     Roo.log(v);
13935                     _this.tickItems.splice(k, 1);
13936                     
13937                     if(typeof(e) == 'undefined' && view == false){
13938                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13939                     }
13940                     
13941                     rm = true;
13942                     return;
13943                 }
13944             });
13945             
13946             if(rm){
13947                 return;
13948             }
13949             
13950             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13951                 this.tickItems.push(r.data);
13952             }
13953             
13954             if(typeof(e) == 'undefined' && view == false){
13955                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13956             }
13957                     
13958             return;
13959         }
13960         
13961         if(r){
13962             this.onSelect(r, index);
13963         }
13964         if(doFocus !== false && !this.blockFocus){
13965             this.inputEl().focus();
13966         }
13967     },
13968
13969     // private
13970     restrictHeight : function(){
13971         //this.innerList.dom.style.height = '';
13972         //var inner = this.innerList.dom;
13973         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13974         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13975         //this.list.beginUpdate();
13976         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13977         this.list.alignTo(this.inputEl(), this.listAlign);
13978         this.list.alignTo(this.inputEl(), this.listAlign);
13979         //this.list.endUpdate();
13980     },
13981
13982     // private
13983     onEmptyResults : function(){
13984         
13985         if(this.tickable && this.editable){
13986             this.hasFocus = false;
13987             this.restrictHeight();
13988             return;
13989         }
13990         
13991         this.collapse();
13992     },
13993
13994     /**
13995      * Returns true if the dropdown list is expanded, else false.
13996      */
13997     isExpanded : function(){
13998         return this.list.isVisible();
13999     },
14000
14001     /**
14002      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14003      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14004      * @param {String} value The data value of the item to select
14005      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14006      * selected item if it is not currently in view (defaults to true)
14007      * @return {Boolean} True if the value matched an item in the list, else false
14008      */
14009     selectByValue : function(v, scrollIntoView){
14010         if(v !== undefined && v !== null){
14011             var r = this.findRecord(this.valueField || this.displayField, v);
14012             if(r){
14013                 this.select(this.store.indexOf(r), scrollIntoView);
14014                 return true;
14015             }
14016         }
14017         return false;
14018     },
14019
14020     /**
14021      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14022      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14023      * @param {Number} index The zero-based index of the list item to select
14024      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14025      * selected item if it is not currently in view (defaults to true)
14026      */
14027     select : function(index, scrollIntoView){
14028         this.selectedIndex = index;
14029         this.view.select(index);
14030         if(scrollIntoView !== false){
14031             var el = this.view.getNode(index);
14032             /*
14033              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14034              */
14035             if(el){
14036                 this.list.scrollChildIntoView(el, false);
14037             }
14038         }
14039     },
14040
14041     // private
14042     selectNext : function(){
14043         var ct = this.store.getCount();
14044         if(ct > 0){
14045             if(this.selectedIndex == -1){
14046                 this.select(0);
14047             }else if(this.selectedIndex < ct-1){
14048                 this.select(this.selectedIndex+1);
14049             }
14050         }
14051     },
14052
14053     // private
14054     selectPrev : function(){
14055         var ct = this.store.getCount();
14056         if(ct > 0){
14057             if(this.selectedIndex == -1){
14058                 this.select(0);
14059             }else if(this.selectedIndex != 0){
14060                 this.select(this.selectedIndex-1);
14061             }
14062         }
14063     },
14064
14065     // private
14066     onKeyUp : function(e){
14067         if(this.editable !== false && !e.isSpecialKey()){
14068             this.lastKey = e.getKey();
14069             this.dqTask.delay(this.queryDelay);
14070         }
14071     },
14072
14073     // private
14074     validateBlur : function(){
14075         return !this.list || !this.list.isVisible();   
14076     },
14077
14078     // private
14079     initQuery : function(){
14080         
14081         var v = this.getRawValue();
14082         
14083         if(this.tickable && this.editable){
14084             v = this.tickableInputEl().getValue();
14085         }
14086         
14087         this.doQuery(v);
14088     },
14089
14090     // private
14091     doForce : function(){
14092         if(this.inputEl().dom.value.length > 0){
14093             this.inputEl().dom.value =
14094                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14095              
14096         }
14097     },
14098
14099     /**
14100      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14101      * query allowing the query action to be canceled if needed.
14102      * @param {String} query The SQL query to execute
14103      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14104      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14105      * saved in the current store (defaults to false)
14106      */
14107     doQuery : function(q, forceAll){
14108         
14109         if(q === undefined || q === null){
14110             q = '';
14111         }
14112         var qe = {
14113             query: q,
14114             forceAll: forceAll,
14115             combo: this,
14116             cancel:false
14117         };
14118         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14119             return false;
14120         }
14121         q = qe.query;
14122         
14123         forceAll = qe.forceAll;
14124         if(forceAll === true || (q.length >= this.minChars)){
14125             
14126             this.hasQuery = true;
14127             
14128             if(this.lastQuery != q || this.alwaysQuery){
14129                 this.lastQuery = q;
14130                 if(this.mode == 'local'){
14131                     this.selectedIndex = -1;
14132                     if(forceAll){
14133                         this.store.clearFilter();
14134                     }else{
14135                         
14136                         if(this.specialFilter){
14137                             this.fireEvent('specialfilter', this);
14138                             this.onLoad();
14139                             return;
14140                         }
14141                         
14142                         this.store.filter(this.displayField, q);
14143                     }
14144                     
14145                     this.store.fireEvent("datachanged", this.store);
14146                     
14147                     this.onLoad();
14148                     
14149                     
14150                 }else{
14151                     
14152                     this.store.baseParams[this.queryParam] = q;
14153                     
14154                     var options = {params : this.getParams(q)};
14155                     
14156                     if(this.loadNext){
14157                         options.add = true;
14158                         options.params.start = this.page * this.pageSize;
14159                     }
14160                     
14161                     this.store.load(options);
14162                     
14163                     /*
14164                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14165                      *  we should expand the list on onLoad
14166                      *  so command out it
14167                      */
14168 //                    this.expand();
14169                 }
14170             }else{
14171                 this.selectedIndex = -1;
14172                 this.onLoad();   
14173             }
14174         }
14175         
14176         this.loadNext = false;
14177     },
14178     
14179     // private
14180     getParams : function(q){
14181         var p = {};
14182         //p[this.queryParam] = q;
14183         
14184         if(this.pageSize){
14185             p.start = 0;
14186             p.limit = this.pageSize;
14187         }
14188         return p;
14189     },
14190
14191     /**
14192      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14193      */
14194     collapse : function(){
14195         if(!this.isExpanded()){
14196             return;
14197         }
14198         
14199         this.list.hide();
14200         
14201         this.hasFocus = false;
14202         
14203         if(this.tickable){
14204             this.okBtn.hide();
14205             this.cancelBtn.hide();
14206             this.trigger.show();
14207             
14208             if(this.editable){
14209                 this.tickableInputEl().dom.value = '';
14210                 this.tickableInputEl().blur();
14211             }
14212             
14213         }
14214         
14215         Roo.get(document).un('mousedown', this.collapseIf, this);
14216         Roo.get(document).un('mousewheel', this.collapseIf, this);
14217         if (!this.editable) {
14218             Roo.get(document).un('keydown', this.listKeyPress, this);
14219         }
14220         this.fireEvent('collapse', this);
14221         
14222         this.validate();
14223     },
14224
14225     // private
14226     collapseIf : function(e){
14227         var in_combo  = e.within(this.el);
14228         var in_list =  e.within(this.list);
14229         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14230         
14231         if (in_combo || in_list || is_list) {
14232             //e.stopPropagation();
14233             return;
14234         }
14235         
14236         if(this.tickable){
14237             this.onTickableFooterButtonClick(e, false, false);
14238         }
14239
14240         this.collapse();
14241         
14242     },
14243
14244     /**
14245      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14246      */
14247     expand : function(){
14248        
14249         if(this.isExpanded() || !this.hasFocus){
14250             return;
14251         }
14252         
14253         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14254         this.list.setWidth(lw);
14255         
14256         Roo.log('expand');
14257         
14258         this.list.show();
14259         
14260         this.restrictHeight();
14261         
14262         if(this.tickable){
14263             
14264             this.tickItems = Roo.apply([], this.item);
14265             
14266             this.okBtn.show();
14267             this.cancelBtn.show();
14268             this.trigger.hide();
14269             
14270             if(this.editable){
14271                 this.tickableInputEl().focus();
14272             }
14273             
14274         }
14275         
14276         Roo.get(document).on('mousedown', this.collapseIf, this);
14277         Roo.get(document).on('mousewheel', this.collapseIf, this);
14278         if (!this.editable) {
14279             Roo.get(document).on('keydown', this.listKeyPress, this);
14280         }
14281         
14282         this.fireEvent('expand', this);
14283     },
14284
14285     // private
14286     // Implements the default empty TriggerField.onTriggerClick function
14287     onTriggerClick : function(e)
14288     {
14289         Roo.log('trigger click');
14290         
14291         if(this.disabled || !this.triggerList){
14292             return;
14293         }
14294         
14295         this.page = 0;
14296         this.loadNext = false;
14297         
14298         if(this.isExpanded()){
14299             this.collapse();
14300             if (!this.blockFocus) {
14301                 this.inputEl().focus();
14302             }
14303             
14304         }else {
14305             this.hasFocus = true;
14306             if(this.triggerAction == 'all') {
14307                 this.doQuery(this.allQuery, true);
14308             } else {
14309                 this.doQuery(this.getRawValue());
14310             }
14311             if (!this.blockFocus) {
14312                 this.inputEl().focus();
14313             }
14314         }
14315     },
14316     
14317     onTickableTriggerClick : function(e)
14318     {
14319         if(this.disabled){
14320             return;
14321         }
14322         
14323         this.page = 0;
14324         this.loadNext = false;
14325         this.hasFocus = true;
14326         
14327         if(this.triggerAction == 'all') {
14328             this.doQuery(this.allQuery, true);
14329         } else {
14330             this.doQuery(this.getRawValue());
14331         }
14332     },
14333     
14334     onSearchFieldClick : function(e)
14335     {
14336         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14337             this.onTickableFooterButtonClick(e, false, false);
14338             return;
14339         }
14340         
14341         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14342             return;
14343         }
14344         
14345         this.page = 0;
14346         this.loadNext = false;
14347         this.hasFocus = true;
14348         
14349         if(this.triggerAction == 'all') {
14350             this.doQuery(this.allQuery, true);
14351         } else {
14352             this.doQuery(this.getRawValue());
14353         }
14354     },
14355     
14356     listKeyPress : function(e)
14357     {
14358         //Roo.log('listkeypress');
14359         // scroll to first matching element based on key pres..
14360         if (e.isSpecialKey()) {
14361             return false;
14362         }
14363         var k = String.fromCharCode(e.getKey()).toUpperCase();
14364         //Roo.log(k);
14365         var match  = false;
14366         var csel = this.view.getSelectedNodes();
14367         var cselitem = false;
14368         if (csel.length) {
14369             var ix = this.view.indexOf(csel[0]);
14370             cselitem  = this.store.getAt(ix);
14371             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14372                 cselitem = false;
14373             }
14374             
14375         }
14376         
14377         this.store.each(function(v) { 
14378             if (cselitem) {
14379                 // start at existing selection.
14380                 if (cselitem.id == v.id) {
14381                     cselitem = false;
14382                 }
14383                 return true;
14384             }
14385                 
14386             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14387                 match = this.store.indexOf(v);
14388                 return false;
14389             }
14390             return true;
14391         }, this);
14392         
14393         if (match === false) {
14394             return true; // no more action?
14395         }
14396         // scroll to?
14397         this.view.select(match);
14398         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14399         sn.scrollIntoView(sn.dom.parentNode, false);
14400     },
14401     
14402     onViewScroll : function(e, t){
14403         
14404         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){
14405             return;
14406         }
14407         
14408         this.hasQuery = true;
14409         
14410         this.loading = this.list.select('.loading', true).first();
14411         
14412         if(this.loading === null){
14413             this.list.createChild({
14414                 tag: 'div',
14415                 cls: 'loading roo-select2-more-results roo-select2-active',
14416                 html: 'Loading more results...'
14417             });
14418             
14419             this.loading = this.list.select('.loading', true).first();
14420             
14421             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14422             
14423             this.loading.hide();
14424         }
14425         
14426         this.loading.show();
14427         
14428         var _combo = this;
14429         
14430         this.page++;
14431         this.loadNext = true;
14432         
14433         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14434         
14435         return;
14436     },
14437     
14438     addItem : function(o)
14439     {   
14440         var dv = ''; // display value
14441         
14442         if (this.displayField) {
14443             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14444         } else {
14445             // this is an error condition!!!
14446             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14447         }
14448         
14449         if(!dv.length){
14450             return;
14451         }
14452         
14453         var choice = this.choices.createChild({
14454             tag: 'li',
14455             cls: 'roo-select2-search-choice',
14456             cn: [
14457                 {
14458                     tag: 'div',
14459                     html: dv
14460                 },
14461                 {
14462                     tag: 'a',
14463                     href: '#',
14464                     cls: 'roo-select2-search-choice-close fa fa-times',
14465                     tabindex: '-1'
14466                 }
14467             ]
14468             
14469         }, this.searchField);
14470         
14471         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14472         
14473         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14474         
14475         this.item.push(o);
14476         
14477         this.lastData = o;
14478         
14479         this.syncValue();
14480         
14481         this.inputEl().dom.value = '';
14482         
14483         this.validate();
14484     },
14485     
14486     onRemoveItem : function(e, _self, o)
14487     {
14488         e.preventDefault();
14489         
14490         this.lastItem = Roo.apply([], this.item);
14491         
14492         var index = this.item.indexOf(o.data) * 1;
14493         
14494         if( index < 0){
14495             Roo.log('not this item?!');
14496             return;
14497         }
14498         
14499         this.item.splice(index, 1);
14500         o.item.remove();
14501         
14502         this.syncValue();
14503         
14504         this.fireEvent('remove', this, e);
14505         
14506         this.validate();
14507         
14508     },
14509     
14510     syncValue : function()
14511     {
14512         if(!this.item.length){
14513             this.clearValue();
14514             return;
14515         }
14516             
14517         var value = [];
14518         var _this = this;
14519         Roo.each(this.item, function(i){
14520             if(_this.valueField){
14521                 value.push(i[_this.valueField]);
14522                 return;
14523             }
14524
14525             value.push(i);
14526         });
14527
14528         this.value = value.join(',');
14529
14530         if(this.hiddenField){
14531             this.hiddenField.dom.value = this.value;
14532         }
14533         
14534         this.store.fireEvent("datachanged", this.store);
14535         
14536         this.validate();
14537     },
14538     
14539     clearItem : function()
14540     {
14541         if(!this.multiple){
14542             return;
14543         }
14544         
14545         this.item = [];
14546         
14547         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14548            c.remove();
14549         });
14550         
14551         this.syncValue();
14552         
14553         this.validate();
14554         
14555         if(this.tickable && !Roo.isTouch){
14556             this.view.refresh();
14557         }
14558     },
14559     
14560     inputEl: function ()
14561     {
14562         if(Roo.isIOS && this.useNativeIOS){
14563             return this.el.select('select.roo-ios-select', true).first();
14564         }
14565         
14566         if(Roo.isTouch && this.mobileTouchView){
14567             return this.el.select('input.form-control',true).first();
14568         }
14569         
14570         if(this.tickable){
14571             return this.searchField;
14572         }
14573         
14574         return this.el.select('input.form-control',true).first();
14575     },
14576     
14577     onTickableFooterButtonClick : function(e, btn, el)
14578     {
14579         e.preventDefault();
14580         
14581         this.lastItem = Roo.apply([], this.item);
14582         
14583         if(btn && btn.name == 'cancel'){
14584             this.tickItems = Roo.apply([], this.item);
14585             this.collapse();
14586             return;
14587         }
14588         
14589         this.clearItem();
14590         
14591         var _this = this;
14592         
14593         Roo.each(this.tickItems, function(o){
14594             _this.addItem(o);
14595         });
14596         
14597         this.collapse();
14598         
14599     },
14600     
14601     validate : function()
14602     {
14603         var v = this.getRawValue();
14604         
14605         if(this.multiple){
14606             v = this.getValue();
14607         }
14608         
14609         if(this.disabled || this.allowBlank || v.length){
14610             this.markValid();
14611             return true;
14612         }
14613         
14614         this.markInvalid();
14615         return false;
14616     },
14617     
14618     tickableInputEl : function()
14619     {
14620         if(!this.tickable || !this.editable){
14621             return this.inputEl();
14622         }
14623         
14624         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14625     },
14626     
14627     
14628     getAutoCreateTouchView : function()
14629     {
14630         var id = Roo.id();
14631         
14632         var cfg = {
14633             cls: 'form-group' //input-group
14634         };
14635         
14636         var input =  {
14637             tag: 'input',
14638             id : id,
14639             type : this.inputType,
14640             cls : 'form-control x-combo-noedit',
14641             autocomplete: 'new-password',
14642             placeholder : this.placeholder || '',
14643             readonly : true
14644         };
14645         
14646         if (this.name) {
14647             input.name = this.name;
14648         }
14649         
14650         if (this.size) {
14651             input.cls += ' input-' + this.size;
14652         }
14653         
14654         if (this.disabled) {
14655             input.disabled = true;
14656         }
14657         
14658         var inputblock = {
14659             cls : '',
14660             cn : [
14661                 input
14662             ]
14663         };
14664         
14665         if(this.before){
14666             inputblock.cls += ' input-group';
14667             
14668             inputblock.cn.unshift({
14669                 tag :'span',
14670                 cls : 'input-group-addon',
14671                 html : this.before
14672             });
14673         }
14674         
14675         if(this.removable && !this.multiple){
14676             inputblock.cls += ' roo-removable';
14677             
14678             inputblock.cn.push({
14679                 tag: 'button',
14680                 html : 'x',
14681                 cls : 'roo-combo-removable-btn close'
14682             });
14683         }
14684
14685         if(this.hasFeedback && !this.allowBlank){
14686             
14687             inputblock.cls += ' has-feedback';
14688             
14689             inputblock.cn.push({
14690                 tag: 'span',
14691                 cls: 'glyphicon form-control-feedback'
14692             });
14693             
14694         }
14695         
14696         if (this.after) {
14697             
14698             inputblock.cls += (this.before) ? '' : ' input-group';
14699             
14700             inputblock.cn.push({
14701                 tag :'span',
14702                 cls : 'input-group-addon',
14703                 html : this.after
14704             });
14705         }
14706
14707         var box = {
14708             tag: 'div',
14709             cn: [
14710                 {
14711                     tag: 'input',
14712                     type : 'hidden',
14713                     cls: 'form-hidden-field'
14714                 },
14715                 inputblock
14716             ]
14717             
14718         };
14719         
14720         if(this.multiple){
14721             box = {
14722                 tag: 'div',
14723                 cn: [
14724                     {
14725                         tag: 'input',
14726                         type : 'hidden',
14727                         cls: 'form-hidden-field'
14728                     },
14729                     {
14730                         tag: 'ul',
14731                         cls: 'roo-select2-choices',
14732                         cn:[
14733                             {
14734                                 tag: 'li',
14735                                 cls: 'roo-select2-search-field',
14736                                 cn: [
14737
14738                                     inputblock
14739                                 ]
14740                             }
14741                         ]
14742                     }
14743                 ]
14744             }
14745         };
14746         
14747         var combobox = {
14748             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14749             cn: [
14750                 box
14751             ]
14752         };
14753         
14754         if(!this.multiple && this.showToggleBtn){
14755             
14756             var caret = {
14757                         tag: 'span',
14758                         cls: 'caret'
14759             };
14760             
14761             if (this.caret != false) {
14762                 caret = {
14763                      tag: 'i',
14764                      cls: 'fa fa-' + this.caret
14765                 };
14766                 
14767             }
14768             
14769             combobox.cn.push({
14770                 tag :'span',
14771                 cls : 'input-group-addon btn dropdown-toggle',
14772                 cn : [
14773                     caret,
14774                     {
14775                         tag: 'span',
14776                         cls: 'combobox-clear',
14777                         cn  : [
14778                             {
14779                                 tag : 'i',
14780                                 cls: 'icon-remove'
14781                             }
14782                         ]
14783                     }
14784                 ]
14785
14786             })
14787         }
14788         
14789         if(this.multiple){
14790             combobox.cls += ' roo-select2-container-multi';
14791         }
14792         
14793         var align = this.labelAlign || this.parentLabelAlign();
14794         
14795         if (align ==='left' && this.fieldLabel.length) {
14796
14797             cfg.cn = [
14798                 {
14799                    tag : 'i',
14800                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14801                    tooltip : 'This field is required'
14802                 },
14803                 {
14804                     tag: 'label',
14805                     cls : 'control-label',
14806                     html : this.fieldLabel
14807
14808                 },
14809                 {
14810                     cls : '', 
14811                     cn: [
14812                         combobox
14813                     ]
14814                 }
14815             ];
14816             
14817             var labelCfg = cfg.cn[1];
14818             var contentCfg = cfg.cn[2];
14819             
14820
14821             if(this.indicatorpos == 'right'){
14822                 cfg.cn = [
14823                     {
14824                         tag: 'label',
14825                         'for' :  id,
14826                         cls : 'control-label',
14827                         cn : [
14828                             {
14829                                 tag : 'span',
14830                                 html : this.fieldLabel
14831                             },
14832                             {
14833                                 tag : 'i',
14834                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14835                                 tooltip : 'This field is required'
14836                             }
14837                         ]
14838                     },
14839                     {
14840                         cls : "",
14841                         cn: [
14842                             combobox
14843                         ]
14844                     }
14845
14846                 ];
14847                 
14848                 labelCfg = cfg.cn[0];
14849                 contentCfg = cfg.cn[1];
14850             }
14851             
14852            
14853             
14854             if(this.labelWidth > 12){
14855                 labelCfg.style = "width: " + this.labelWidth + 'px';
14856             }
14857             
14858             if(this.labelWidth < 13 && this.labelmd == 0){
14859                 this.labelmd = this.labelWidth;
14860             }
14861             
14862             if(this.labellg > 0){
14863                 labelCfg.cls += ' col-lg-' + this.labellg;
14864                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14865             }
14866             
14867             if(this.labelmd > 0){
14868                 labelCfg.cls += ' col-md-' + this.labelmd;
14869                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14870             }
14871             
14872             if(this.labelsm > 0){
14873                 labelCfg.cls += ' col-sm-' + this.labelsm;
14874                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14875             }
14876             
14877             if(this.labelxs > 0){
14878                 labelCfg.cls += ' col-xs-' + this.labelxs;
14879                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14880             }
14881                 
14882                 
14883         } else if ( this.fieldLabel.length) {
14884             cfg.cn = [
14885                 {
14886                    tag : 'i',
14887                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14888                    tooltip : 'This field is required'
14889                 },
14890                 {
14891                     tag: 'label',
14892                     cls : 'control-label',
14893                     html : this.fieldLabel
14894
14895                 },
14896                 {
14897                     cls : '', 
14898                     cn: [
14899                         combobox
14900                     ]
14901                 }
14902             ];
14903             
14904             if(this.indicatorpos == 'right'){
14905                 cfg.cn = [
14906                     {
14907                         tag: 'label',
14908                         cls : 'control-label',
14909                         html : this.fieldLabel,
14910                         cn : [
14911                             {
14912                                tag : 'i',
14913                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14914                                tooltip : 'This field is required'
14915                             }
14916                         ]
14917                     },
14918                     {
14919                         cls : '', 
14920                         cn: [
14921                             combobox
14922                         ]
14923                     }
14924                 ];
14925             }
14926         } else {
14927             cfg.cn = combobox;    
14928         }
14929         
14930         
14931         var settings = this;
14932         
14933         ['xs','sm','md','lg'].map(function(size){
14934             if (settings[size]) {
14935                 cfg.cls += ' col-' + size + '-' + settings[size];
14936             }
14937         });
14938         
14939         return cfg;
14940     },
14941     
14942     initTouchView : function()
14943     {
14944         this.renderTouchView();
14945         
14946         this.touchViewEl.on('scroll', function(){
14947             this.el.dom.scrollTop = 0;
14948         }, this);
14949         
14950         this.originalValue = this.getValue();
14951         
14952         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14953         
14954         this.inputEl().on("click", this.showTouchView, this);
14955         if (this.triggerEl) {
14956             this.triggerEl.on("click", this.showTouchView, this);
14957         }
14958         
14959         
14960         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14961         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14962         
14963         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14964         
14965         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14966         this.store.on('load', this.onTouchViewLoad, this);
14967         this.store.on('loadexception', this.onTouchViewLoadException, this);
14968         
14969         if(this.hiddenName){
14970             
14971             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14972             
14973             this.hiddenField.dom.value =
14974                 this.hiddenValue !== undefined ? this.hiddenValue :
14975                 this.value !== undefined ? this.value : '';
14976         
14977             this.el.dom.removeAttribute('name');
14978             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14979         }
14980         
14981         if(this.multiple){
14982             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14983             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14984         }
14985         
14986         if(this.removable && !this.multiple){
14987             var close = this.closeTriggerEl();
14988             if(close){
14989                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14990                 close.on('click', this.removeBtnClick, this, close);
14991             }
14992         }
14993         /*
14994          * fix the bug in Safari iOS8
14995          */
14996         this.inputEl().on("focus", function(e){
14997             document.activeElement.blur();
14998         }, this);
14999         
15000         return;
15001         
15002         
15003     },
15004     
15005     renderTouchView : function()
15006     {
15007         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15008         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15009         
15010         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15011         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15012         
15013         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15014         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15015         this.touchViewBodyEl.setStyle('overflow', 'auto');
15016         
15017         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15018         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15019         
15020         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15021         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15022         
15023     },
15024     
15025     showTouchView : function()
15026     {
15027         if(this.disabled){
15028             return;
15029         }
15030         
15031         this.touchViewHeaderEl.hide();
15032
15033         if(this.modalTitle.length){
15034             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15035             this.touchViewHeaderEl.show();
15036         }
15037
15038         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15039         this.touchViewEl.show();
15040
15041         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15042         
15043         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15044         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15045
15046         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15047
15048         if(this.modalTitle.length){
15049             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15050         }
15051         
15052         this.touchViewBodyEl.setHeight(bodyHeight);
15053
15054         if(this.animate){
15055             var _this = this;
15056             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15057         }else{
15058             this.touchViewEl.addClass('in');
15059         }
15060
15061         this.doTouchViewQuery();
15062         
15063     },
15064     
15065     hideTouchView : function()
15066     {
15067         this.touchViewEl.removeClass('in');
15068
15069         if(this.animate){
15070             var _this = this;
15071             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15072         }else{
15073             this.touchViewEl.setStyle('display', 'none');
15074         }
15075         
15076     },
15077     
15078     setTouchViewValue : function()
15079     {
15080         if(this.multiple){
15081             this.clearItem();
15082         
15083             var _this = this;
15084
15085             Roo.each(this.tickItems, function(o){
15086                 this.addItem(o);
15087             }, this);
15088         }
15089         
15090         this.hideTouchView();
15091     },
15092     
15093     doTouchViewQuery : function()
15094     {
15095         var qe = {
15096             query: '',
15097             forceAll: true,
15098             combo: this,
15099             cancel:false
15100         };
15101         
15102         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15103             return false;
15104         }
15105         
15106         if(!this.alwaysQuery || this.mode == 'local'){
15107             this.onTouchViewLoad();
15108             return;
15109         }
15110         
15111         this.store.load();
15112     },
15113     
15114     onTouchViewBeforeLoad : function(combo,opts)
15115     {
15116         return;
15117     },
15118
15119     // private
15120     onTouchViewLoad : function()
15121     {
15122         if(this.store.getCount() < 1){
15123             this.onTouchViewEmptyResults();
15124             return;
15125         }
15126         
15127         this.clearTouchView();
15128         
15129         var rawValue = this.getRawValue();
15130         
15131         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15132         
15133         this.tickItems = [];
15134         
15135         this.store.data.each(function(d, rowIndex){
15136             var row = this.touchViewListGroup.createChild(template);
15137             
15138             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15139                 row.addClass(d.data.cls);
15140             }
15141             
15142             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15143                 var cfg = {
15144                     data : d.data,
15145                     html : d.data[this.displayField]
15146                 };
15147                 
15148                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15149                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15150                 }
15151             }
15152             row.removeClass('selected');
15153             if(!this.multiple && this.valueField &&
15154                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15155             {
15156                 // radio buttons..
15157                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15158                 row.addClass('selected');
15159             }
15160             
15161             if(this.multiple && this.valueField &&
15162                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15163             {
15164                 
15165                 // checkboxes...
15166                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15167                 this.tickItems.push(d.data);
15168             }
15169             
15170             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15171             
15172         }, this);
15173         
15174         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15175         
15176         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15177
15178         if(this.modalTitle.length){
15179             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15180         }
15181
15182         var listHeight = this.touchViewListGroup.getHeight();
15183         
15184         var _this = this;
15185         
15186         if(firstChecked && listHeight > bodyHeight){
15187             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15188         }
15189         
15190     },
15191     
15192     onTouchViewLoadException : function()
15193     {
15194         this.hideTouchView();
15195     },
15196     
15197     onTouchViewEmptyResults : function()
15198     {
15199         this.clearTouchView();
15200         
15201         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15202         
15203         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15204         
15205     },
15206     
15207     clearTouchView : function()
15208     {
15209         this.touchViewListGroup.dom.innerHTML = '';
15210     },
15211     
15212     onTouchViewClick : function(e, el, o)
15213     {
15214         e.preventDefault();
15215         
15216         var row = o.row;
15217         var rowIndex = o.rowIndex;
15218         
15219         var r = this.store.getAt(rowIndex);
15220         
15221         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15222             
15223             if(!this.multiple){
15224                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15225                     c.dom.removeAttribute('checked');
15226                 }, this);
15227
15228                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15229
15230                 this.setFromData(r.data);
15231
15232                 var close = this.closeTriggerEl();
15233
15234                 if(close){
15235                     close.show();
15236                 }
15237
15238                 this.hideTouchView();
15239
15240                 this.fireEvent('select', this, r, rowIndex);
15241
15242                 return;
15243             }
15244
15245             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15246                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15247                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15248                 return;
15249             }
15250
15251             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15252             this.addItem(r.data);
15253             this.tickItems.push(r.data);
15254         }
15255     },
15256     
15257     getAutoCreateNativeIOS : function()
15258     {
15259         var cfg = {
15260             cls: 'form-group' //input-group,
15261         };
15262         
15263         var combobox =  {
15264             tag: 'select',
15265             cls : 'roo-ios-select'
15266         };
15267         
15268         if (this.name) {
15269             combobox.name = this.name;
15270         }
15271         
15272         if (this.disabled) {
15273             combobox.disabled = true;
15274         }
15275         
15276         var settings = this;
15277         
15278         ['xs','sm','md','lg'].map(function(size){
15279             if (settings[size]) {
15280                 cfg.cls += ' col-' + size + '-' + settings[size];
15281             }
15282         });
15283         
15284         cfg.cn = combobox;
15285         
15286         return cfg;
15287         
15288     },
15289     
15290     initIOSView : function()
15291     {
15292         this.store.on('load', this.onIOSViewLoad, this);
15293         
15294         return;
15295     },
15296     
15297     onIOSViewLoad : function()
15298     {
15299         if(this.store.getCount() < 1){
15300             return;
15301         }
15302         
15303         this.clearIOSView();
15304         
15305         if(this.allowBlank) {
15306             
15307             var default_text = '-- SELECT --';
15308             
15309             if(this.placeholder.length){
15310                 default_text = this.placeholder;
15311             }
15312             
15313             if(this.emptyTitle.length){
15314                 default_text += ' - ' + this.emptyTitle + ' -';
15315             }
15316             
15317             var opt = this.inputEl().createChild({
15318                 tag: 'option',
15319                 value : 0,
15320                 html : default_text
15321             });
15322             
15323             var o = {};
15324             o[this.valueField] = 0;
15325             o[this.displayField] = default_text;
15326             
15327             this.ios_options.push({
15328                 data : o,
15329                 el : opt
15330             });
15331             
15332         }
15333         
15334         this.store.data.each(function(d, rowIndex){
15335             
15336             var html = '';
15337             
15338             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15339                 html = d.data[this.displayField];
15340             }
15341             
15342             var value = '';
15343             
15344             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15345                 value = d.data[this.valueField];
15346             }
15347             
15348             var option = {
15349                 tag: 'option',
15350                 value : value,
15351                 html : html
15352             };
15353             
15354             if(this.value == d.data[this.valueField]){
15355                 option['selected'] = true;
15356             }
15357             
15358             var opt = this.inputEl().createChild(option);
15359             
15360             this.ios_options.push({
15361                 data : d.data,
15362                 el : opt
15363             });
15364             
15365         }, this);
15366         
15367         this.inputEl().on('change', function(){
15368            this.fireEvent('select', this);
15369         }, this);
15370         
15371     },
15372     
15373     clearIOSView: function()
15374     {
15375         this.inputEl().dom.innerHTML = '';
15376         
15377         this.ios_options = [];
15378     },
15379     
15380     setIOSValue: function(v)
15381     {
15382         this.value = v;
15383         
15384         if(!this.ios_options){
15385             return;
15386         }
15387         
15388         Roo.each(this.ios_options, function(opts){
15389            
15390            opts.el.dom.removeAttribute('selected');
15391            
15392            if(opts.data[this.valueField] != v){
15393                return;
15394            }
15395            
15396            opts.el.dom.setAttribute('selected', true);
15397            
15398         }, this);
15399     }
15400
15401     /** 
15402     * @cfg {Boolean} grow 
15403     * @hide 
15404     */
15405     /** 
15406     * @cfg {Number} growMin 
15407     * @hide 
15408     */
15409     /** 
15410     * @cfg {Number} growMax 
15411     * @hide 
15412     */
15413     /**
15414      * @hide
15415      * @method autoSize
15416      */
15417 });
15418
15419 Roo.apply(Roo.bootstrap.ComboBox,  {
15420     
15421     header : {
15422         tag: 'div',
15423         cls: 'modal-header',
15424         cn: [
15425             {
15426                 tag: 'h4',
15427                 cls: 'modal-title'
15428             }
15429         ]
15430     },
15431     
15432     body : {
15433         tag: 'div',
15434         cls: 'modal-body',
15435         cn: [
15436             {
15437                 tag: 'ul',
15438                 cls: 'list-group'
15439             }
15440         ]
15441     },
15442     
15443     listItemRadio : {
15444         tag: 'li',
15445         cls: 'list-group-item',
15446         cn: [
15447             {
15448                 tag: 'span',
15449                 cls: 'roo-combobox-list-group-item-value'
15450             },
15451             {
15452                 tag: 'div',
15453                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15454                 cn: [
15455                     {
15456                         tag: 'input',
15457                         type: 'radio'
15458                     },
15459                     {
15460                         tag: 'label'
15461                     }
15462                 ]
15463             }
15464         ]
15465     },
15466     
15467     listItemCheckbox : {
15468         tag: 'li',
15469         cls: 'list-group-item',
15470         cn: [
15471             {
15472                 tag: 'span',
15473                 cls: 'roo-combobox-list-group-item-value'
15474             },
15475             {
15476                 tag: 'div',
15477                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15478                 cn: [
15479                     {
15480                         tag: 'input',
15481                         type: 'checkbox'
15482                     },
15483                     {
15484                         tag: 'label'
15485                     }
15486                 ]
15487             }
15488         ]
15489     },
15490     
15491     emptyResult : {
15492         tag: 'div',
15493         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15494     },
15495     
15496     footer : {
15497         tag: 'div',
15498         cls: 'modal-footer',
15499         cn: [
15500             {
15501                 tag: 'div',
15502                 cls: 'row',
15503                 cn: [
15504                     {
15505                         tag: 'div',
15506                         cls: 'col-xs-6 text-left',
15507                         cn: {
15508                             tag: 'button',
15509                             cls: 'btn btn-danger roo-touch-view-cancel',
15510                             html: 'Cancel'
15511                         }
15512                     },
15513                     {
15514                         tag: 'div',
15515                         cls: 'col-xs-6 text-right',
15516                         cn: {
15517                             tag: 'button',
15518                             cls: 'btn btn-success roo-touch-view-ok',
15519                             html: 'OK'
15520                         }
15521                     }
15522                 ]
15523             }
15524         ]
15525         
15526     }
15527 });
15528
15529 Roo.apply(Roo.bootstrap.ComboBox,  {
15530     
15531     touchViewTemplate : {
15532         tag: 'div',
15533         cls: 'modal fade roo-combobox-touch-view',
15534         cn: [
15535             {
15536                 tag: 'div',
15537                 cls: 'modal-dialog',
15538                 style : 'position:fixed', // we have to fix position....
15539                 cn: [
15540                     {
15541                         tag: 'div',
15542                         cls: 'modal-content',
15543                         cn: [
15544                             Roo.bootstrap.ComboBox.header,
15545                             Roo.bootstrap.ComboBox.body,
15546                             Roo.bootstrap.ComboBox.footer
15547                         ]
15548                     }
15549                 ]
15550             }
15551         ]
15552     }
15553 });/*
15554  * Based on:
15555  * Ext JS Library 1.1.1
15556  * Copyright(c) 2006-2007, Ext JS, LLC.
15557  *
15558  * Originally Released Under LGPL - original licence link has changed is not relivant.
15559  *
15560  * Fork - LGPL
15561  * <script type="text/javascript">
15562  */
15563
15564 /**
15565  * @class Roo.View
15566  * @extends Roo.util.Observable
15567  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15568  * This class also supports single and multi selection modes. <br>
15569  * Create a data model bound view:
15570  <pre><code>
15571  var store = new Roo.data.Store(...);
15572
15573  var view = new Roo.View({
15574     el : "my-element",
15575     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15576  
15577     singleSelect: true,
15578     selectedClass: "ydataview-selected",
15579     store: store
15580  });
15581
15582  // listen for node click?
15583  view.on("click", function(vw, index, node, e){
15584  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15585  });
15586
15587  // load XML data
15588  dataModel.load("foobar.xml");
15589  </code></pre>
15590  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15591  * <br><br>
15592  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15593  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15594  * 
15595  * Note: old style constructor is still suported (container, template, config)
15596  * 
15597  * @constructor
15598  * Create a new View
15599  * @param {Object} config The config object
15600  * 
15601  */
15602 Roo.View = function(config, depreciated_tpl, depreciated_config){
15603     
15604     this.parent = false;
15605     
15606     if (typeof(depreciated_tpl) == 'undefined') {
15607         // new way.. - universal constructor.
15608         Roo.apply(this, config);
15609         this.el  = Roo.get(this.el);
15610     } else {
15611         // old format..
15612         this.el  = Roo.get(config);
15613         this.tpl = depreciated_tpl;
15614         Roo.apply(this, depreciated_config);
15615     }
15616     this.wrapEl  = this.el.wrap().wrap();
15617     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15618     
15619     
15620     if(typeof(this.tpl) == "string"){
15621         this.tpl = new Roo.Template(this.tpl);
15622     } else {
15623         // support xtype ctors..
15624         this.tpl = new Roo.factory(this.tpl, Roo);
15625     }
15626     
15627     
15628     this.tpl.compile();
15629     
15630     /** @private */
15631     this.addEvents({
15632         /**
15633          * @event beforeclick
15634          * Fires before a click is processed. Returns false to cancel the default action.
15635          * @param {Roo.View} this
15636          * @param {Number} index The index of the target node
15637          * @param {HTMLElement} node The target node
15638          * @param {Roo.EventObject} e The raw event object
15639          */
15640             "beforeclick" : true,
15641         /**
15642          * @event click
15643          * Fires when a template node is clicked.
15644          * @param {Roo.View} this
15645          * @param {Number} index The index of the target node
15646          * @param {HTMLElement} node The target node
15647          * @param {Roo.EventObject} e The raw event object
15648          */
15649             "click" : true,
15650         /**
15651          * @event dblclick
15652          * Fires when a template node is double clicked.
15653          * @param {Roo.View} this
15654          * @param {Number} index The index of the target node
15655          * @param {HTMLElement} node The target node
15656          * @param {Roo.EventObject} e The raw event object
15657          */
15658             "dblclick" : true,
15659         /**
15660          * @event contextmenu
15661          * Fires when a template node is right clicked.
15662          * @param {Roo.View} this
15663          * @param {Number} index The index of the target node
15664          * @param {HTMLElement} node The target node
15665          * @param {Roo.EventObject} e The raw event object
15666          */
15667             "contextmenu" : true,
15668         /**
15669          * @event selectionchange
15670          * Fires when the selected nodes change.
15671          * @param {Roo.View} this
15672          * @param {Array} selections Array of the selected nodes
15673          */
15674             "selectionchange" : true,
15675     
15676         /**
15677          * @event beforeselect
15678          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15679          * @param {Roo.View} this
15680          * @param {HTMLElement} node The node to be selected
15681          * @param {Array} selections Array of currently selected nodes
15682          */
15683             "beforeselect" : true,
15684         /**
15685          * @event preparedata
15686          * Fires on every row to render, to allow you to change the data.
15687          * @param {Roo.View} this
15688          * @param {Object} data to be rendered (change this)
15689          */
15690           "preparedata" : true
15691           
15692           
15693         });
15694
15695
15696
15697     this.el.on({
15698         "click": this.onClick,
15699         "dblclick": this.onDblClick,
15700         "contextmenu": this.onContextMenu,
15701         scope:this
15702     });
15703
15704     this.selections = [];
15705     this.nodes = [];
15706     this.cmp = new Roo.CompositeElementLite([]);
15707     if(this.store){
15708         this.store = Roo.factory(this.store, Roo.data);
15709         this.setStore(this.store, true);
15710     }
15711     
15712     if ( this.footer && this.footer.xtype) {
15713            
15714          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15715         
15716         this.footer.dataSource = this.store;
15717         this.footer.container = fctr;
15718         this.footer = Roo.factory(this.footer, Roo);
15719         fctr.insertFirst(this.el);
15720         
15721         // this is a bit insane - as the paging toolbar seems to detach the el..
15722 //        dom.parentNode.parentNode.parentNode
15723          // they get detached?
15724     }
15725     
15726     
15727     Roo.View.superclass.constructor.call(this);
15728     
15729     
15730 };
15731
15732 Roo.extend(Roo.View, Roo.util.Observable, {
15733     
15734      /**
15735      * @cfg {Roo.data.Store} store Data store to load data from.
15736      */
15737     store : false,
15738     
15739     /**
15740      * @cfg {String|Roo.Element} el The container element.
15741      */
15742     el : '',
15743     
15744     /**
15745      * @cfg {String|Roo.Template} tpl The template used by this View 
15746      */
15747     tpl : false,
15748     /**
15749      * @cfg {String} dataName the named area of the template to use as the data area
15750      *                          Works with domtemplates roo-name="name"
15751      */
15752     dataName: false,
15753     /**
15754      * @cfg {String} selectedClass The css class to add to selected nodes
15755      */
15756     selectedClass : "x-view-selected",
15757      /**
15758      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15759      */
15760     emptyText : "",
15761     
15762     /**
15763      * @cfg {String} text to display on mask (default Loading)
15764      */
15765     mask : false,
15766     /**
15767      * @cfg {Boolean} multiSelect Allow multiple selection
15768      */
15769     multiSelect : false,
15770     /**
15771      * @cfg {Boolean} singleSelect Allow single selection
15772      */
15773     singleSelect:  false,
15774     
15775     /**
15776      * @cfg {Boolean} toggleSelect - selecting 
15777      */
15778     toggleSelect : false,
15779     
15780     /**
15781      * @cfg {Boolean} tickable - selecting 
15782      */
15783     tickable : false,
15784     
15785     /**
15786      * Returns the element this view is bound to.
15787      * @return {Roo.Element}
15788      */
15789     getEl : function(){
15790         return this.wrapEl;
15791     },
15792     
15793     
15794
15795     /**
15796      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15797      */
15798     refresh : function(){
15799         //Roo.log('refresh');
15800         var t = this.tpl;
15801         
15802         // if we are using something like 'domtemplate', then
15803         // the what gets used is:
15804         // t.applySubtemplate(NAME, data, wrapping data..)
15805         // the outer template then get' applied with
15806         //     the store 'extra data'
15807         // and the body get's added to the
15808         //      roo-name="data" node?
15809         //      <span class='roo-tpl-{name}'></span> ?????
15810         
15811         
15812         
15813         this.clearSelections();
15814         this.el.update("");
15815         var html = [];
15816         var records = this.store.getRange();
15817         if(records.length < 1) {
15818             
15819             // is this valid??  = should it render a template??
15820             
15821             this.el.update(this.emptyText);
15822             return;
15823         }
15824         var el = this.el;
15825         if (this.dataName) {
15826             this.el.update(t.apply(this.store.meta)); //????
15827             el = this.el.child('.roo-tpl-' + this.dataName);
15828         }
15829         
15830         for(var i = 0, len = records.length; i < len; i++){
15831             var data = this.prepareData(records[i].data, i, records[i]);
15832             this.fireEvent("preparedata", this, data, i, records[i]);
15833             
15834             var d = Roo.apply({}, data);
15835             
15836             if(this.tickable){
15837                 Roo.apply(d, {'roo-id' : Roo.id()});
15838                 
15839                 var _this = this;
15840             
15841                 Roo.each(this.parent.item, function(item){
15842                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15843                         return;
15844                     }
15845                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15846                 });
15847             }
15848             
15849             html[html.length] = Roo.util.Format.trim(
15850                 this.dataName ?
15851                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15852                     t.apply(d)
15853             );
15854         }
15855         
15856         
15857         
15858         el.update(html.join(""));
15859         this.nodes = el.dom.childNodes;
15860         this.updateIndexes(0);
15861     },
15862     
15863
15864     /**
15865      * Function to override to reformat the data that is sent to
15866      * the template for each node.
15867      * DEPRICATED - use the preparedata event handler.
15868      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15869      * a JSON object for an UpdateManager bound view).
15870      */
15871     prepareData : function(data, index, record)
15872     {
15873         this.fireEvent("preparedata", this, data, index, record);
15874         return data;
15875     },
15876
15877     onUpdate : function(ds, record){
15878         // Roo.log('on update');   
15879         this.clearSelections();
15880         var index = this.store.indexOf(record);
15881         var n = this.nodes[index];
15882         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15883         n.parentNode.removeChild(n);
15884         this.updateIndexes(index, index);
15885     },
15886
15887     
15888     
15889 // --------- FIXME     
15890     onAdd : function(ds, records, index)
15891     {
15892         //Roo.log(['on Add', ds, records, index] );        
15893         this.clearSelections();
15894         if(this.nodes.length == 0){
15895             this.refresh();
15896             return;
15897         }
15898         var n = this.nodes[index];
15899         for(var i = 0, len = records.length; i < len; i++){
15900             var d = this.prepareData(records[i].data, i, records[i]);
15901             if(n){
15902                 this.tpl.insertBefore(n, d);
15903             }else{
15904                 
15905                 this.tpl.append(this.el, d);
15906             }
15907         }
15908         this.updateIndexes(index);
15909     },
15910
15911     onRemove : function(ds, record, index){
15912        // Roo.log('onRemove');
15913         this.clearSelections();
15914         var el = this.dataName  ?
15915             this.el.child('.roo-tpl-' + this.dataName) :
15916             this.el; 
15917         
15918         el.dom.removeChild(this.nodes[index]);
15919         this.updateIndexes(index);
15920     },
15921
15922     /**
15923      * Refresh an individual node.
15924      * @param {Number} index
15925      */
15926     refreshNode : function(index){
15927         this.onUpdate(this.store, this.store.getAt(index));
15928     },
15929
15930     updateIndexes : function(startIndex, endIndex){
15931         var ns = this.nodes;
15932         startIndex = startIndex || 0;
15933         endIndex = endIndex || ns.length - 1;
15934         for(var i = startIndex; i <= endIndex; i++){
15935             ns[i].nodeIndex = i;
15936         }
15937     },
15938
15939     /**
15940      * Changes the data store this view uses and refresh the view.
15941      * @param {Store} store
15942      */
15943     setStore : function(store, initial){
15944         if(!initial && this.store){
15945             this.store.un("datachanged", this.refresh);
15946             this.store.un("add", this.onAdd);
15947             this.store.un("remove", this.onRemove);
15948             this.store.un("update", this.onUpdate);
15949             this.store.un("clear", this.refresh);
15950             this.store.un("beforeload", this.onBeforeLoad);
15951             this.store.un("load", this.onLoad);
15952             this.store.un("loadexception", this.onLoad);
15953         }
15954         if(store){
15955           
15956             store.on("datachanged", this.refresh, this);
15957             store.on("add", this.onAdd, this);
15958             store.on("remove", this.onRemove, this);
15959             store.on("update", this.onUpdate, this);
15960             store.on("clear", this.refresh, this);
15961             store.on("beforeload", this.onBeforeLoad, this);
15962             store.on("load", this.onLoad, this);
15963             store.on("loadexception", this.onLoad, this);
15964         }
15965         
15966         if(store){
15967             this.refresh();
15968         }
15969     },
15970     /**
15971      * onbeforeLoad - masks the loading area.
15972      *
15973      */
15974     onBeforeLoad : function(store,opts)
15975     {
15976          //Roo.log('onBeforeLoad');   
15977         if (!opts.add) {
15978             this.el.update("");
15979         }
15980         this.el.mask(this.mask ? this.mask : "Loading" ); 
15981     },
15982     onLoad : function ()
15983     {
15984         this.el.unmask();
15985     },
15986     
15987
15988     /**
15989      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15990      * @param {HTMLElement} node
15991      * @return {HTMLElement} The template node
15992      */
15993     findItemFromChild : function(node){
15994         var el = this.dataName  ?
15995             this.el.child('.roo-tpl-' + this.dataName,true) :
15996             this.el.dom; 
15997         
15998         if(!node || node.parentNode == el){
15999                     return node;
16000             }
16001             var p = node.parentNode;
16002             while(p && p != el){
16003             if(p.parentNode == el){
16004                 return p;
16005             }
16006             p = p.parentNode;
16007         }
16008             return null;
16009     },
16010
16011     /** @ignore */
16012     onClick : function(e){
16013         var item = this.findItemFromChild(e.getTarget());
16014         if(item){
16015             var index = this.indexOf(item);
16016             if(this.onItemClick(item, index, e) !== false){
16017                 this.fireEvent("click", this, index, item, e);
16018             }
16019         }else{
16020             this.clearSelections();
16021         }
16022     },
16023
16024     /** @ignore */
16025     onContextMenu : function(e){
16026         var item = this.findItemFromChild(e.getTarget());
16027         if(item){
16028             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16029         }
16030     },
16031
16032     /** @ignore */
16033     onDblClick : function(e){
16034         var item = this.findItemFromChild(e.getTarget());
16035         if(item){
16036             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16037         }
16038     },
16039
16040     onItemClick : function(item, index, e)
16041     {
16042         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16043             return false;
16044         }
16045         if (this.toggleSelect) {
16046             var m = this.isSelected(item) ? 'unselect' : 'select';
16047             //Roo.log(m);
16048             var _t = this;
16049             _t[m](item, true, false);
16050             return true;
16051         }
16052         if(this.multiSelect || this.singleSelect){
16053             if(this.multiSelect && e.shiftKey && this.lastSelection){
16054                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16055             }else{
16056                 this.select(item, this.multiSelect && e.ctrlKey);
16057                 this.lastSelection = item;
16058             }
16059             
16060             if(!this.tickable){
16061                 e.preventDefault();
16062             }
16063             
16064         }
16065         return true;
16066     },
16067
16068     /**
16069      * Get the number of selected nodes.
16070      * @return {Number}
16071      */
16072     getSelectionCount : function(){
16073         return this.selections.length;
16074     },
16075
16076     /**
16077      * Get the currently selected nodes.
16078      * @return {Array} An array of HTMLElements
16079      */
16080     getSelectedNodes : function(){
16081         return this.selections;
16082     },
16083
16084     /**
16085      * Get the indexes of the selected nodes.
16086      * @return {Array}
16087      */
16088     getSelectedIndexes : function(){
16089         var indexes = [], s = this.selections;
16090         for(var i = 0, len = s.length; i < len; i++){
16091             indexes.push(s[i].nodeIndex);
16092         }
16093         return indexes;
16094     },
16095
16096     /**
16097      * Clear all selections
16098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16099      */
16100     clearSelections : function(suppressEvent){
16101         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16102             this.cmp.elements = this.selections;
16103             this.cmp.removeClass(this.selectedClass);
16104             this.selections = [];
16105             if(!suppressEvent){
16106                 this.fireEvent("selectionchange", this, this.selections);
16107             }
16108         }
16109     },
16110
16111     /**
16112      * Returns true if the passed node is selected
16113      * @param {HTMLElement/Number} node The node or node index
16114      * @return {Boolean}
16115      */
16116     isSelected : function(node){
16117         var s = this.selections;
16118         if(s.length < 1){
16119             return false;
16120         }
16121         node = this.getNode(node);
16122         return s.indexOf(node) !== -1;
16123     },
16124
16125     /**
16126      * Selects nodes.
16127      * @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
16128      * @param {Boolean} keepExisting (optional) true to keep existing selections
16129      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16130      */
16131     select : function(nodeInfo, keepExisting, suppressEvent){
16132         if(nodeInfo instanceof Array){
16133             if(!keepExisting){
16134                 this.clearSelections(true);
16135             }
16136             for(var i = 0, len = nodeInfo.length; i < len; i++){
16137                 this.select(nodeInfo[i], true, true);
16138             }
16139             return;
16140         } 
16141         var node = this.getNode(nodeInfo);
16142         if(!node || this.isSelected(node)){
16143             return; // already selected.
16144         }
16145         if(!keepExisting){
16146             this.clearSelections(true);
16147         }
16148         
16149         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16150             Roo.fly(node).addClass(this.selectedClass);
16151             this.selections.push(node);
16152             if(!suppressEvent){
16153                 this.fireEvent("selectionchange", this, this.selections);
16154             }
16155         }
16156         
16157         
16158     },
16159       /**
16160      * Unselects nodes.
16161      * @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
16162      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16163      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16164      */
16165     unselect : function(nodeInfo, keepExisting, suppressEvent)
16166     {
16167         if(nodeInfo instanceof Array){
16168             Roo.each(this.selections, function(s) {
16169                 this.unselect(s, nodeInfo);
16170             }, this);
16171             return;
16172         }
16173         var node = this.getNode(nodeInfo);
16174         if(!node || !this.isSelected(node)){
16175             //Roo.log("not selected");
16176             return; // not selected.
16177         }
16178         // fireevent???
16179         var ns = [];
16180         Roo.each(this.selections, function(s) {
16181             if (s == node ) {
16182                 Roo.fly(node).removeClass(this.selectedClass);
16183
16184                 return;
16185             }
16186             ns.push(s);
16187         },this);
16188         
16189         this.selections= ns;
16190         this.fireEvent("selectionchange", this, this.selections);
16191     },
16192
16193     /**
16194      * Gets a template node.
16195      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16196      * @return {HTMLElement} The node or null if it wasn't found
16197      */
16198     getNode : function(nodeInfo){
16199         if(typeof nodeInfo == "string"){
16200             return document.getElementById(nodeInfo);
16201         }else if(typeof nodeInfo == "number"){
16202             return this.nodes[nodeInfo];
16203         }
16204         return nodeInfo;
16205     },
16206
16207     /**
16208      * Gets a range template nodes.
16209      * @param {Number} startIndex
16210      * @param {Number} endIndex
16211      * @return {Array} An array of nodes
16212      */
16213     getNodes : function(start, end){
16214         var ns = this.nodes;
16215         start = start || 0;
16216         end = typeof end == "undefined" ? ns.length - 1 : end;
16217         var nodes = [];
16218         if(start <= end){
16219             for(var i = start; i <= end; i++){
16220                 nodes.push(ns[i]);
16221             }
16222         } else{
16223             for(var i = start; i >= end; i--){
16224                 nodes.push(ns[i]);
16225             }
16226         }
16227         return nodes;
16228     },
16229
16230     /**
16231      * Finds the index of the passed node
16232      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16233      * @return {Number} The index of the node or -1
16234      */
16235     indexOf : function(node){
16236         node = this.getNode(node);
16237         if(typeof node.nodeIndex == "number"){
16238             return node.nodeIndex;
16239         }
16240         var ns = this.nodes;
16241         for(var i = 0, len = ns.length; i < len; i++){
16242             if(ns[i] == node){
16243                 return i;
16244             }
16245         }
16246         return -1;
16247     }
16248 });
16249 /*
16250  * - LGPL
16251  *
16252  * based on jquery fullcalendar
16253  * 
16254  */
16255
16256 Roo.bootstrap = Roo.bootstrap || {};
16257 /**
16258  * @class Roo.bootstrap.Calendar
16259  * @extends Roo.bootstrap.Component
16260  * Bootstrap Calendar class
16261  * @cfg {Boolean} loadMask (true|false) default false
16262  * @cfg {Object} header generate the user specific header of the calendar, default false
16263
16264  * @constructor
16265  * Create a new Container
16266  * @param {Object} config The config object
16267  */
16268
16269
16270
16271 Roo.bootstrap.Calendar = function(config){
16272     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16273      this.addEvents({
16274         /**
16275              * @event select
16276              * Fires when a date is selected
16277              * @param {DatePicker} this
16278              * @param {Date} date The selected date
16279              */
16280         'select': true,
16281         /**
16282              * @event monthchange
16283              * Fires when the displayed month changes 
16284              * @param {DatePicker} this
16285              * @param {Date} date The selected month
16286              */
16287         'monthchange': true,
16288         /**
16289              * @event evententer
16290              * Fires when mouse over an event
16291              * @param {Calendar} this
16292              * @param {event} Event
16293              */
16294         'evententer': true,
16295         /**
16296              * @event eventleave
16297              * Fires when the mouse leaves an
16298              * @param {Calendar} this
16299              * @param {event}
16300              */
16301         'eventleave': true,
16302         /**
16303              * @event eventclick
16304              * Fires when the mouse click an
16305              * @param {Calendar} this
16306              * @param {event}
16307              */
16308         'eventclick': true
16309         
16310     });
16311
16312 };
16313
16314 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16315     
16316      /**
16317      * @cfg {Number} startDay
16318      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16319      */
16320     startDay : 0,
16321     
16322     loadMask : false,
16323     
16324     header : false,
16325       
16326     getAutoCreate : function(){
16327         
16328         
16329         var fc_button = function(name, corner, style, content ) {
16330             return Roo.apply({},{
16331                 tag : 'span',
16332                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16333                          (corner.length ?
16334                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16335                             ''
16336                         ),
16337                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16338                 unselectable: 'on'
16339             });
16340         };
16341         
16342         var header = {};
16343         
16344         if(!this.header){
16345             header = {
16346                 tag : 'table',
16347                 cls : 'fc-header',
16348                 style : 'width:100%',
16349                 cn : [
16350                     {
16351                         tag: 'tr',
16352                         cn : [
16353                             {
16354                                 tag : 'td',
16355                                 cls : 'fc-header-left',
16356                                 cn : [
16357                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16358                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16359                                     { tag: 'span', cls: 'fc-header-space' },
16360                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16361
16362
16363                                 ]
16364                             },
16365
16366                             {
16367                                 tag : 'td',
16368                                 cls : 'fc-header-center',
16369                                 cn : [
16370                                     {
16371                                         tag: 'span',
16372                                         cls: 'fc-header-title',
16373                                         cn : {
16374                                             tag: 'H2',
16375                                             html : 'month / year'
16376                                         }
16377                                     }
16378
16379                                 ]
16380                             },
16381                             {
16382                                 tag : 'td',
16383                                 cls : 'fc-header-right',
16384                                 cn : [
16385                               /*      fc_button('month', 'left', '', 'month' ),
16386                                     fc_button('week', '', '', 'week' ),
16387                                     fc_button('day', 'right', '', 'day' )
16388                                 */    
16389
16390                                 ]
16391                             }
16392
16393                         ]
16394                     }
16395                 ]
16396             };
16397         }
16398         
16399         header = this.header;
16400         
16401        
16402         var cal_heads = function() {
16403             var ret = [];
16404             // fixme - handle this.
16405             
16406             for (var i =0; i < Date.dayNames.length; i++) {
16407                 var d = Date.dayNames[i];
16408                 ret.push({
16409                     tag: 'th',
16410                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16411                     html : d.substring(0,3)
16412                 });
16413                 
16414             }
16415             ret[0].cls += ' fc-first';
16416             ret[6].cls += ' fc-last';
16417             return ret;
16418         };
16419         var cal_cell = function(n) {
16420             return  {
16421                 tag: 'td',
16422                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16423                 cn : [
16424                     {
16425                         cn : [
16426                             {
16427                                 cls: 'fc-day-number',
16428                                 html: 'D'
16429                             },
16430                             {
16431                                 cls: 'fc-day-content',
16432                              
16433                                 cn : [
16434                                      {
16435                                         style: 'position: relative;' // height: 17px;
16436                                     }
16437                                 ]
16438                             }
16439                             
16440                             
16441                         ]
16442                     }
16443                 ]
16444                 
16445             }
16446         };
16447         var cal_rows = function() {
16448             
16449             var ret = [];
16450             for (var r = 0; r < 6; r++) {
16451                 var row= {
16452                     tag : 'tr',
16453                     cls : 'fc-week',
16454                     cn : []
16455                 };
16456                 
16457                 for (var i =0; i < Date.dayNames.length; i++) {
16458                     var d = Date.dayNames[i];
16459                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16460
16461                 }
16462                 row.cn[0].cls+=' fc-first';
16463                 row.cn[0].cn[0].style = 'min-height:90px';
16464                 row.cn[6].cls+=' fc-last';
16465                 ret.push(row);
16466                 
16467             }
16468             ret[0].cls += ' fc-first';
16469             ret[4].cls += ' fc-prev-last';
16470             ret[5].cls += ' fc-last';
16471             return ret;
16472             
16473         };
16474         
16475         var cal_table = {
16476             tag: 'table',
16477             cls: 'fc-border-separate',
16478             style : 'width:100%',
16479             cellspacing  : 0,
16480             cn : [
16481                 { 
16482                     tag: 'thead',
16483                     cn : [
16484                         { 
16485                             tag: 'tr',
16486                             cls : 'fc-first fc-last',
16487                             cn : cal_heads()
16488                         }
16489                     ]
16490                 },
16491                 { 
16492                     tag: 'tbody',
16493                     cn : cal_rows()
16494                 }
16495                   
16496             ]
16497         };
16498          
16499          var cfg = {
16500             cls : 'fc fc-ltr',
16501             cn : [
16502                 header,
16503                 {
16504                     cls : 'fc-content',
16505                     style : "position: relative;",
16506                     cn : [
16507                         {
16508                             cls : 'fc-view fc-view-month fc-grid',
16509                             style : 'position: relative',
16510                             unselectable : 'on',
16511                             cn : [
16512                                 {
16513                                     cls : 'fc-event-container',
16514                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16515                                 },
16516                                 cal_table
16517                             ]
16518                         }
16519                     ]
16520     
16521                 }
16522            ] 
16523             
16524         };
16525         
16526          
16527         
16528         return cfg;
16529     },
16530     
16531     
16532     initEvents : function()
16533     {
16534         if(!this.store){
16535             throw "can not find store for calendar";
16536         }
16537         
16538         var mark = {
16539             tag: "div",
16540             cls:"x-dlg-mask",
16541             style: "text-align:center",
16542             cn: [
16543                 {
16544                     tag: "div",
16545                     style: "background-color:white;width:50%;margin:250 auto",
16546                     cn: [
16547                         {
16548                             tag: "img",
16549                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16550                         },
16551                         {
16552                             tag: "span",
16553                             html: "Loading"
16554                         }
16555                         
16556                     ]
16557                 }
16558             ]
16559         };
16560         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16561         
16562         var size = this.el.select('.fc-content', true).first().getSize();
16563         this.maskEl.setSize(size.width, size.height);
16564         this.maskEl.enableDisplayMode("block");
16565         if(!this.loadMask){
16566             this.maskEl.hide();
16567         }
16568         
16569         this.store = Roo.factory(this.store, Roo.data);
16570         this.store.on('load', this.onLoad, this);
16571         this.store.on('beforeload', this.onBeforeLoad, this);
16572         
16573         this.resize();
16574         
16575         this.cells = this.el.select('.fc-day',true);
16576         //Roo.log(this.cells);
16577         this.textNodes = this.el.query('.fc-day-number');
16578         this.cells.addClassOnOver('fc-state-hover');
16579         
16580         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16581         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16582         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16583         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16584         
16585         this.on('monthchange', this.onMonthChange, this);
16586         
16587         this.update(new Date().clearTime());
16588     },
16589     
16590     resize : function() {
16591         var sz  = this.el.getSize();
16592         
16593         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16594         this.el.select('.fc-day-content div',true).setHeight(34);
16595     },
16596     
16597     
16598     // private
16599     showPrevMonth : function(e){
16600         this.update(this.activeDate.add("mo", -1));
16601     },
16602     showToday : function(e){
16603         this.update(new Date().clearTime());
16604     },
16605     // private
16606     showNextMonth : function(e){
16607         this.update(this.activeDate.add("mo", 1));
16608     },
16609
16610     // private
16611     showPrevYear : function(){
16612         this.update(this.activeDate.add("y", -1));
16613     },
16614
16615     // private
16616     showNextYear : function(){
16617         this.update(this.activeDate.add("y", 1));
16618     },
16619
16620     
16621    // private
16622     update : function(date)
16623     {
16624         var vd = this.activeDate;
16625         this.activeDate = date;
16626 //        if(vd && this.el){
16627 //            var t = date.getTime();
16628 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16629 //                Roo.log('using add remove');
16630 //                
16631 //                this.fireEvent('monthchange', this, date);
16632 //                
16633 //                this.cells.removeClass("fc-state-highlight");
16634 //                this.cells.each(function(c){
16635 //                   if(c.dateValue == t){
16636 //                       c.addClass("fc-state-highlight");
16637 //                       setTimeout(function(){
16638 //                            try{c.dom.firstChild.focus();}catch(e){}
16639 //                       }, 50);
16640 //                       return false;
16641 //                   }
16642 //                   return true;
16643 //                });
16644 //                return;
16645 //            }
16646 //        }
16647         
16648         var days = date.getDaysInMonth();
16649         
16650         var firstOfMonth = date.getFirstDateOfMonth();
16651         var startingPos = firstOfMonth.getDay()-this.startDay;
16652         
16653         if(startingPos < this.startDay){
16654             startingPos += 7;
16655         }
16656         
16657         var pm = date.add(Date.MONTH, -1);
16658         var prevStart = pm.getDaysInMonth()-startingPos;
16659 //        
16660         this.cells = this.el.select('.fc-day',true);
16661         this.textNodes = this.el.query('.fc-day-number');
16662         this.cells.addClassOnOver('fc-state-hover');
16663         
16664         var cells = this.cells.elements;
16665         var textEls = this.textNodes;
16666         
16667         Roo.each(cells, function(cell){
16668             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16669         });
16670         
16671         days += startingPos;
16672
16673         // convert everything to numbers so it's fast
16674         var day = 86400000;
16675         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16676         //Roo.log(d);
16677         //Roo.log(pm);
16678         //Roo.log(prevStart);
16679         
16680         var today = new Date().clearTime().getTime();
16681         var sel = date.clearTime().getTime();
16682         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16683         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16684         var ddMatch = this.disabledDatesRE;
16685         var ddText = this.disabledDatesText;
16686         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16687         var ddaysText = this.disabledDaysText;
16688         var format = this.format;
16689         
16690         var setCellClass = function(cal, cell){
16691             cell.row = 0;
16692             cell.events = [];
16693             cell.more = [];
16694             //Roo.log('set Cell Class');
16695             cell.title = "";
16696             var t = d.getTime();
16697             
16698             //Roo.log(d);
16699             
16700             cell.dateValue = t;
16701             if(t == today){
16702                 cell.className += " fc-today";
16703                 cell.className += " fc-state-highlight";
16704                 cell.title = cal.todayText;
16705             }
16706             if(t == sel){
16707                 // disable highlight in other month..
16708                 //cell.className += " fc-state-highlight";
16709                 
16710             }
16711             // disabling
16712             if(t < min) {
16713                 cell.className = " fc-state-disabled";
16714                 cell.title = cal.minText;
16715                 return;
16716             }
16717             if(t > max) {
16718                 cell.className = " fc-state-disabled";
16719                 cell.title = cal.maxText;
16720                 return;
16721             }
16722             if(ddays){
16723                 if(ddays.indexOf(d.getDay()) != -1){
16724                     cell.title = ddaysText;
16725                     cell.className = " fc-state-disabled";
16726                 }
16727             }
16728             if(ddMatch && format){
16729                 var fvalue = d.dateFormat(format);
16730                 if(ddMatch.test(fvalue)){
16731                     cell.title = ddText.replace("%0", fvalue);
16732                     cell.className = " fc-state-disabled";
16733                 }
16734             }
16735             
16736             if (!cell.initialClassName) {
16737                 cell.initialClassName = cell.dom.className;
16738             }
16739             
16740             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16741         };
16742
16743         var i = 0;
16744         
16745         for(; i < startingPos; i++) {
16746             textEls[i].innerHTML = (++prevStart);
16747             d.setDate(d.getDate()+1);
16748             
16749             cells[i].className = "fc-past fc-other-month";
16750             setCellClass(this, cells[i]);
16751         }
16752         
16753         var intDay = 0;
16754         
16755         for(; i < days; i++){
16756             intDay = i - startingPos + 1;
16757             textEls[i].innerHTML = (intDay);
16758             d.setDate(d.getDate()+1);
16759             
16760             cells[i].className = ''; // "x-date-active";
16761             setCellClass(this, cells[i]);
16762         }
16763         var extraDays = 0;
16764         
16765         for(; i < 42; i++) {
16766             textEls[i].innerHTML = (++extraDays);
16767             d.setDate(d.getDate()+1);
16768             
16769             cells[i].className = "fc-future fc-other-month";
16770             setCellClass(this, cells[i]);
16771         }
16772         
16773         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16774         
16775         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16776         
16777         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16778         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16779         
16780         if(totalRows != 6){
16781             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16782             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16783         }
16784         
16785         this.fireEvent('monthchange', this, date);
16786         
16787         
16788         /*
16789         if(!this.internalRender){
16790             var main = this.el.dom.firstChild;
16791             var w = main.offsetWidth;
16792             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16793             Roo.fly(main).setWidth(w);
16794             this.internalRender = true;
16795             // opera does not respect the auto grow header center column
16796             // then, after it gets a width opera refuses to recalculate
16797             // without a second pass
16798             if(Roo.isOpera && !this.secondPass){
16799                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16800                 this.secondPass = true;
16801                 this.update.defer(10, this, [date]);
16802             }
16803         }
16804         */
16805         
16806     },
16807     
16808     findCell : function(dt) {
16809         dt = dt.clearTime().getTime();
16810         var ret = false;
16811         this.cells.each(function(c){
16812             //Roo.log("check " +c.dateValue + '?=' + dt);
16813             if(c.dateValue == dt){
16814                 ret = c;
16815                 return false;
16816             }
16817             return true;
16818         });
16819         
16820         return ret;
16821     },
16822     
16823     findCells : function(ev) {
16824         var s = ev.start.clone().clearTime().getTime();
16825        // Roo.log(s);
16826         var e= ev.end.clone().clearTime().getTime();
16827        // Roo.log(e);
16828         var ret = [];
16829         this.cells.each(function(c){
16830              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16831             
16832             if(c.dateValue > e){
16833                 return ;
16834             }
16835             if(c.dateValue < s){
16836                 return ;
16837             }
16838             ret.push(c);
16839         });
16840         
16841         return ret;    
16842     },
16843     
16844 //    findBestRow: function(cells)
16845 //    {
16846 //        var ret = 0;
16847 //        
16848 //        for (var i =0 ; i < cells.length;i++) {
16849 //            ret  = Math.max(cells[i].rows || 0,ret);
16850 //        }
16851 //        return ret;
16852 //        
16853 //    },
16854     
16855     
16856     addItem : function(ev)
16857     {
16858         // look for vertical location slot in
16859         var cells = this.findCells(ev);
16860         
16861 //        ev.row = this.findBestRow(cells);
16862         
16863         // work out the location.
16864         
16865         var crow = false;
16866         var rows = [];
16867         for(var i =0; i < cells.length; i++) {
16868             
16869             cells[i].row = cells[0].row;
16870             
16871             if(i == 0){
16872                 cells[i].row = cells[i].row + 1;
16873             }
16874             
16875             if (!crow) {
16876                 crow = {
16877                     start : cells[i],
16878                     end :  cells[i]
16879                 };
16880                 continue;
16881             }
16882             if (crow.start.getY() == cells[i].getY()) {
16883                 // on same row.
16884                 crow.end = cells[i];
16885                 continue;
16886             }
16887             // different row.
16888             rows.push(crow);
16889             crow = {
16890                 start: cells[i],
16891                 end : cells[i]
16892             };
16893             
16894         }
16895         
16896         rows.push(crow);
16897         ev.els = [];
16898         ev.rows = rows;
16899         ev.cells = cells;
16900         
16901         cells[0].events.push(ev);
16902         
16903         this.calevents.push(ev);
16904     },
16905     
16906     clearEvents: function() {
16907         
16908         if(!this.calevents){
16909             return;
16910         }
16911         
16912         Roo.each(this.cells.elements, function(c){
16913             c.row = 0;
16914             c.events = [];
16915             c.more = [];
16916         });
16917         
16918         Roo.each(this.calevents, function(e) {
16919             Roo.each(e.els, function(el) {
16920                 el.un('mouseenter' ,this.onEventEnter, this);
16921                 el.un('mouseleave' ,this.onEventLeave, this);
16922                 el.remove();
16923             },this);
16924         },this);
16925         
16926         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16927             e.remove();
16928         });
16929         
16930     },
16931     
16932     renderEvents: function()
16933     {   
16934         var _this = this;
16935         
16936         this.cells.each(function(c) {
16937             
16938             if(c.row < 5){
16939                 return;
16940             }
16941             
16942             var ev = c.events;
16943             
16944             var r = 4;
16945             if(c.row != c.events.length){
16946                 r = 4 - (4 - (c.row - c.events.length));
16947             }
16948             
16949             c.events = ev.slice(0, r);
16950             c.more = ev.slice(r);
16951             
16952             if(c.more.length && c.more.length == 1){
16953                 c.events.push(c.more.pop());
16954             }
16955             
16956             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16957             
16958         });
16959             
16960         this.cells.each(function(c) {
16961             
16962             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16963             
16964             
16965             for (var e = 0; e < c.events.length; e++){
16966                 var ev = c.events[e];
16967                 var rows = ev.rows;
16968                 
16969                 for(var i = 0; i < rows.length; i++) {
16970                 
16971                     // how many rows should it span..
16972
16973                     var  cfg = {
16974                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16975                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16976
16977                         unselectable : "on",
16978                         cn : [
16979                             {
16980                                 cls: 'fc-event-inner',
16981                                 cn : [
16982     //                                {
16983     //                                  tag:'span',
16984     //                                  cls: 'fc-event-time',
16985     //                                  html : cells.length > 1 ? '' : ev.time
16986     //                                },
16987                                     {
16988                                       tag:'span',
16989                                       cls: 'fc-event-title',
16990                                       html : String.format('{0}', ev.title)
16991                                     }
16992
16993
16994                                 ]
16995                             },
16996                             {
16997                                 cls: 'ui-resizable-handle ui-resizable-e',
16998                                 html : '&nbsp;&nbsp;&nbsp'
16999                             }
17000
17001                         ]
17002                     };
17003
17004                     if (i == 0) {
17005                         cfg.cls += ' fc-event-start';
17006                     }
17007                     if ((i+1) == rows.length) {
17008                         cfg.cls += ' fc-event-end';
17009                     }
17010
17011                     var ctr = _this.el.select('.fc-event-container',true).first();
17012                     var cg = ctr.createChild(cfg);
17013
17014                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17015                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17016
17017                     var r = (c.more.length) ? 1 : 0;
17018                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17019                     cg.setWidth(ebox.right - sbox.x -2);
17020
17021                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17022                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17023                     cg.on('click', _this.onEventClick, _this, ev);
17024
17025                     ev.els.push(cg);
17026                     
17027                 }
17028                 
17029             }
17030             
17031             
17032             if(c.more.length){
17033                 var  cfg = {
17034                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17035                     style : 'position: absolute',
17036                     unselectable : "on",
17037                     cn : [
17038                         {
17039                             cls: 'fc-event-inner',
17040                             cn : [
17041                                 {
17042                                   tag:'span',
17043                                   cls: 'fc-event-title',
17044                                   html : 'More'
17045                                 }
17046
17047
17048                             ]
17049                         },
17050                         {
17051                             cls: 'ui-resizable-handle ui-resizable-e',
17052                             html : '&nbsp;&nbsp;&nbsp'
17053                         }
17054
17055                     ]
17056                 };
17057
17058                 var ctr = _this.el.select('.fc-event-container',true).first();
17059                 var cg = ctr.createChild(cfg);
17060
17061                 var sbox = c.select('.fc-day-content',true).first().getBox();
17062                 var ebox = c.select('.fc-day-content',true).first().getBox();
17063                 //Roo.log(cg);
17064                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17065                 cg.setWidth(ebox.right - sbox.x -2);
17066
17067                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17068                 
17069             }
17070             
17071         });
17072         
17073         
17074         
17075     },
17076     
17077     onEventEnter: function (e, el,event,d) {
17078         this.fireEvent('evententer', this, el, event);
17079     },
17080     
17081     onEventLeave: function (e, el,event,d) {
17082         this.fireEvent('eventleave', this, el, event);
17083     },
17084     
17085     onEventClick: function (e, el,event,d) {
17086         this.fireEvent('eventclick', this, el, event);
17087     },
17088     
17089     onMonthChange: function () {
17090         this.store.load();
17091     },
17092     
17093     onMoreEventClick: function(e, el, more)
17094     {
17095         var _this = this;
17096         
17097         this.calpopover.placement = 'right';
17098         this.calpopover.setTitle('More');
17099         
17100         this.calpopover.setContent('');
17101         
17102         var ctr = this.calpopover.el.select('.popover-content', true).first();
17103         
17104         Roo.each(more, function(m){
17105             var cfg = {
17106                 cls : 'fc-event-hori fc-event-draggable',
17107                 html : m.title
17108             };
17109             var cg = ctr.createChild(cfg);
17110             
17111             cg.on('click', _this.onEventClick, _this, m);
17112         });
17113         
17114         this.calpopover.show(el);
17115         
17116         
17117     },
17118     
17119     onLoad: function () 
17120     {   
17121         this.calevents = [];
17122         var cal = this;
17123         
17124         if(this.store.getCount() > 0){
17125             this.store.data.each(function(d){
17126                cal.addItem({
17127                     id : d.data.id,
17128                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17129                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17130                     time : d.data.start_time,
17131                     title : d.data.title,
17132                     description : d.data.description,
17133                     venue : d.data.venue
17134                 });
17135             });
17136         }
17137         
17138         this.renderEvents();
17139         
17140         if(this.calevents.length && this.loadMask){
17141             this.maskEl.hide();
17142         }
17143     },
17144     
17145     onBeforeLoad: function()
17146     {
17147         this.clearEvents();
17148         if(this.loadMask){
17149             this.maskEl.show();
17150         }
17151     }
17152 });
17153
17154  
17155  /*
17156  * - LGPL
17157  *
17158  * element
17159  * 
17160  */
17161
17162 /**
17163  * @class Roo.bootstrap.Popover
17164  * @extends Roo.bootstrap.Component
17165  * Bootstrap Popover class
17166  * @cfg {String} html contents of the popover   (or false to use children..)
17167  * @cfg {String} title of popover (or false to hide)
17168  * @cfg {String} placement how it is placed
17169  * @cfg {String} trigger click || hover (or false to trigger manually)
17170  * @cfg {String} over what (parent or false to trigger manually.)
17171  * @cfg {Number} delay - delay before showing
17172  
17173  * @constructor
17174  * Create a new Popover
17175  * @param {Object} config The config object
17176  */
17177
17178 Roo.bootstrap.Popover = function(config){
17179     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17180     
17181     this.addEvents({
17182         // raw events
17183          /**
17184          * @event show
17185          * After the popover show
17186          * 
17187          * @param {Roo.bootstrap.Popover} this
17188          */
17189         "show" : true,
17190         /**
17191          * @event hide
17192          * After the popover hide
17193          * 
17194          * @param {Roo.bootstrap.Popover} this
17195          */
17196         "hide" : true
17197     });
17198 };
17199
17200 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17201     
17202     title: 'Fill in a title',
17203     html: false,
17204     
17205     placement : 'right',
17206     trigger : 'hover', // hover
17207     
17208     delay : 0,
17209     
17210     over: 'parent',
17211     
17212     can_build_overlaid : false,
17213     
17214     getChildContainer : function()
17215     {
17216         return this.el.select('.popover-content',true).first();
17217     },
17218     
17219     getAutoCreate : function(){
17220          
17221         var cfg = {
17222            cls : 'popover roo-dynamic',
17223            style: 'display:block',
17224            cn : [
17225                 {
17226                     cls : 'arrow'
17227                 },
17228                 {
17229                     cls : 'popover-inner',
17230                     cn : [
17231                         {
17232                             tag: 'h3',
17233                             cls: 'popover-title',
17234                             html : this.title
17235                         },
17236                         {
17237                             cls : 'popover-content',
17238                             html : this.html
17239                         }
17240                     ]
17241                     
17242                 }
17243            ]
17244         };
17245         
17246         return cfg;
17247     },
17248     setTitle: function(str)
17249     {
17250         this.title = str;
17251         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17252     },
17253     setContent: function(str)
17254     {
17255         this.html = str;
17256         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17257     },
17258     // as it get's added to the bottom of the page.
17259     onRender : function(ct, position)
17260     {
17261         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17262         if(!this.el){
17263             var cfg = Roo.apply({},  this.getAutoCreate());
17264             cfg.id = Roo.id();
17265             
17266             if (this.cls) {
17267                 cfg.cls += ' ' + this.cls;
17268             }
17269             if (this.style) {
17270                 cfg.style = this.style;
17271             }
17272             //Roo.log("adding to ");
17273             this.el = Roo.get(document.body).createChild(cfg, position);
17274 //            Roo.log(this.el);
17275         }
17276         this.initEvents();
17277     },
17278     
17279     initEvents : function()
17280     {
17281         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17282         this.el.enableDisplayMode('block');
17283         this.el.hide();
17284         if (this.over === false) {
17285             return; 
17286         }
17287         if (this.triggers === false) {
17288             return;
17289         }
17290         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17291         var triggers = this.trigger ? this.trigger.split(' ') : [];
17292         Roo.each(triggers, function(trigger) {
17293         
17294             if (trigger == 'click') {
17295                 on_el.on('click', this.toggle, this);
17296             } else if (trigger != 'manual') {
17297                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17298                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17299       
17300                 on_el.on(eventIn  ,this.enter, this);
17301                 on_el.on(eventOut, this.leave, this);
17302             }
17303         }, this);
17304         
17305     },
17306     
17307     
17308     // private
17309     timeout : null,
17310     hoverState : null,
17311     
17312     toggle : function () {
17313         this.hoverState == 'in' ? this.leave() : this.enter();
17314     },
17315     
17316     enter : function () {
17317         
17318         clearTimeout(this.timeout);
17319     
17320         this.hoverState = 'in';
17321     
17322         if (!this.delay || !this.delay.show) {
17323             this.show();
17324             return;
17325         }
17326         var _t = this;
17327         this.timeout = setTimeout(function () {
17328             if (_t.hoverState == 'in') {
17329                 _t.show();
17330             }
17331         }, this.delay.show)
17332     },
17333     
17334     leave : function() {
17335         clearTimeout(this.timeout);
17336     
17337         this.hoverState = 'out';
17338     
17339         if (!this.delay || !this.delay.hide) {
17340             this.hide();
17341             return;
17342         }
17343         var _t = this;
17344         this.timeout = setTimeout(function () {
17345             if (_t.hoverState == 'out') {
17346                 _t.hide();
17347             }
17348         }, this.delay.hide)
17349     },
17350     
17351     show : function (on_el)
17352     {
17353         if (!on_el) {
17354             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17355         }
17356         
17357         // set content.
17358         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17359         if (this.html !== false) {
17360             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17361         }
17362         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17363         if (!this.title.length) {
17364             this.el.select('.popover-title',true).hide();
17365         }
17366         
17367         var placement = typeof this.placement == 'function' ?
17368             this.placement.call(this, this.el, on_el) :
17369             this.placement;
17370             
17371         var autoToken = /\s?auto?\s?/i;
17372         var autoPlace = autoToken.test(placement);
17373         if (autoPlace) {
17374             placement = placement.replace(autoToken, '') || 'top';
17375         }
17376         
17377         //this.el.detach()
17378         //this.el.setXY([0,0]);
17379         this.el.show();
17380         this.el.dom.style.display='block';
17381         this.el.addClass(placement);
17382         
17383         //this.el.appendTo(on_el);
17384         
17385         var p = this.getPosition();
17386         var box = this.el.getBox();
17387         
17388         if (autoPlace) {
17389             // fixme..
17390         }
17391         var align = Roo.bootstrap.Popover.alignment[placement];
17392         
17393 //        Roo.log(align);
17394         this.el.alignTo(on_el, align[0],align[1]);
17395         //var arrow = this.el.select('.arrow',true).first();
17396         //arrow.set(align[2], 
17397         
17398         this.el.addClass('in');
17399         
17400         
17401         if (this.el.hasClass('fade')) {
17402             // fade it?
17403         }
17404         
17405         this.hoverState = 'in';
17406         
17407         this.fireEvent('show', this);
17408         
17409     },
17410     hide : function()
17411     {
17412         this.el.setXY([0,0]);
17413         this.el.removeClass('in');
17414         this.el.hide();
17415         this.hoverState = null;
17416         
17417         this.fireEvent('hide', this);
17418     }
17419     
17420 });
17421
17422 Roo.bootstrap.Popover.alignment = {
17423     'left' : ['r-l', [-10,0], 'right'],
17424     'right' : ['l-r', [10,0], 'left'],
17425     'bottom' : ['t-b', [0,10], 'top'],
17426     'top' : [ 'b-t', [0,-10], 'bottom']
17427 };
17428
17429  /*
17430  * - LGPL
17431  *
17432  * Progress
17433  * 
17434  */
17435
17436 /**
17437  * @class Roo.bootstrap.Progress
17438  * @extends Roo.bootstrap.Component
17439  * Bootstrap Progress class
17440  * @cfg {Boolean} striped striped of the progress bar
17441  * @cfg {Boolean} active animated of the progress bar
17442  * 
17443  * 
17444  * @constructor
17445  * Create a new Progress
17446  * @param {Object} config The config object
17447  */
17448
17449 Roo.bootstrap.Progress = function(config){
17450     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17451 };
17452
17453 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17454     
17455     striped : false,
17456     active: false,
17457     
17458     getAutoCreate : function(){
17459         var cfg = {
17460             tag: 'div',
17461             cls: 'progress'
17462         };
17463         
17464         
17465         if(this.striped){
17466             cfg.cls += ' progress-striped';
17467         }
17468       
17469         if(this.active){
17470             cfg.cls += ' active';
17471         }
17472         
17473         
17474         return cfg;
17475     }
17476    
17477 });
17478
17479  
17480
17481  /*
17482  * - LGPL
17483  *
17484  * ProgressBar
17485  * 
17486  */
17487
17488 /**
17489  * @class Roo.bootstrap.ProgressBar
17490  * @extends Roo.bootstrap.Component
17491  * Bootstrap ProgressBar class
17492  * @cfg {Number} aria_valuenow aria-value now
17493  * @cfg {Number} aria_valuemin aria-value min
17494  * @cfg {Number} aria_valuemax aria-value max
17495  * @cfg {String} label label for the progress bar
17496  * @cfg {String} panel (success | info | warning | danger )
17497  * @cfg {String} role role of the progress bar
17498  * @cfg {String} sr_only text
17499  * 
17500  * 
17501  * @constructor
17502  * Create a new ProgressBar
17503  * @param {Object} config The config object
17504  */
17505
17506 Roo.bootstrap.ProgressBar = function(config){
17507     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17508 };
17509
17510 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17511     
17512     aria_valuenow : 0,
17513     aria_valuemin : 0,
17514     aria_valuemax : 100,
17515     label : false,
17516     panel : false,
17517     role : false,
17518     sr_only: false,
17519     
17520     getAutoCreate : function()
17521     {
17522         
17523         var cfg = {
17524             tag: 'div',
17525             cls: 'progress-bar',
17526             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17527         };
17528         
17529         if(this.sr_only){
17530             cfg.cn = {
17531                 tag: 'span',
17532                 cls: 'sr-only',
17533                 html: this.sr_only
17534             }
17535         }
17536         
17537         if(this.role){
17538             cfg.role = this.role;
17539         }
17540         
17541         if(this.aria_valuenow){
17542             cfg['aria-valuenow'] = this.aria_valuenow;
17543         }
17544         
17545         if(this.aria_valuemin){
17546             cfg['aria-valuemin'] = this.aria_valuemin;
17547         }
17548         
17549         if(this.aria_valuemax){
17550             cfg['aria-valuemax'] = this.aria_valuemax;
17551         }
17552         
17553         if(this.label && !this.sr_only){
17554             cfg.html = this.label;
17555         }
17556         
17557         if(this.panel){
17558             cfg.cls += ' progress-bar-' + this.panel;
17559         }
17560         
17561         return cfg;
17562     },
17563     
17564     update : function(aria_valuenow)
17565     {
17566         this.aria_valuenow = aria_valuenow;
17567         
17568         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17569     }
17570    
17571 });
17572
17573  
17574
17575  /*
17576  * - LGPL
17577  *
17578  * column
17579  * 
17580  */
17581
17582 /**
17583  * @class Roo.bootstrap.TabGroup
17584  * @extends Roo.bootstrap.Column
17585  * Bootstrap Column class
17586  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17587  * @cfg {Boolean} carousel true to make the group behave like a carousel
17588  * @cfg {Boolean} bullets show bullets for the panels
17589  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17590  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17591  * @cfg {Boolean} showarrow (true|false) show arrow default true
17592  * 
17593  * @constructor
17594  * Create a new TabGroup
17595  * @param {Object} config The config object
17596  */
17597
17598 Roo.bootstrap.TabGroup = function(config){
17599     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17600     if (!this.navId) {
17601         this.navId = Roo.id();
17602     }
17603     this.tabs = [];
17604     Roo.bootstrap.TabGroup.register(this);
17605     
17606 };
17607
17608 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17609     
17610     carousel : false,
17611     transition : false,
17612     bullets : 0,
17613     timer : 0,
17614     autoslide : false,
17615     slideFn : false,
17616     slideOnTouch : false,
17617     showarrow : true,
17618     
17619     getAutoCreate : function()
17620     {
17621         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17622         
17623         cfg.cls += ' tab-content';
17624         
17625         if (this.carousel) {
17626             cfg.cls += ' carousel slide';
17627             
17628             cfg.cn = [{
17629                cls : 'carousel-inner',
17630                cn : []
17631             }];
17632         
17633             if(this.bullets  && !Roo.isTouch){
17634                 
17635                 var bullets = {
17636                     cls : 'carousel-bullets',
17637                     cn : []
17638                 };
17639                
17640                 if(this.bullets_cls){
17641                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17642                 }
17643                 
17644                 bullets.cn.push({
17645                     cls : 'clear'
17646                 });
17647                 
17648                 cfg.cn[0].cn.push(bullets);
17649             }
17650             
17651             if(this.showarrow){
17652                 cfg.cn[0].cn.push({
17653                     tag : 'div',
17654                     class : 'carousel-arrow',
17655                     cn : [
17656                         {
17657                             tag : 'div',
17658                             class : 'carousel-prev',
17659                             cn : [
17660                                 {
17661                                     tag : 'i',
17662                                     class : 'fa fa-chevron-left'
17663                                 }
17664                             ]
17665                         },
17666                         {
17667                             tag : 'div',
17668                             class : 'carousel-next',
17669                             cn : [
17670                                 {
17671                                     tag : 'i',
17672                                     class : 'fa fa-chevron-right'
17673                                 }
17674                             ]
17675                         }
17676                     ]
17677                 });
17678             }
17679             
17680         }
17681         
17682         return cfg;
17683     },
17684     
17685     initEvents:  function()
17686     {
17687 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17688 //            this.el.on("touchstart", this.onTouchStart, this);
17689 //        }
17690         
17691         if(this.autoslide){
17692             var _this = this;
17693             
17694             this.slideFn = window.setInterval(function() {
17695                 _this.showPanelNext();
17696             }, this.timer);
17697         }
17698         
17699         if(this.showarrow){
17700             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17701             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17702         }
17703         
17704         
17705     },
17706     
17707 //    onTouchStart : function(e, el, o)
17708 //    {
17709 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17710 //            return;
17711 //        }
17712 //        
17713 //        this.showPanelNext();
17714 //    },
17715     
17716     
17717     getChildContainer : function()
17718     {
17719         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17720     },
17721     
17722     /**
17723     * register a Navigation item
17724     * @param {Roo.bootstrap.NavItem} the navitem to add
17725     */
17726     register : function(item)
17727     {
17728         this.tabs.push( item);
17729         item.navId = this.navId; // not really needed..
17730         this.addBullet();
17731     
17732     },
17733     
17734     getActivePanel : function()
17735     {
17736         var r = false;
17737         Roo.each(this.tabs, function(t) {
17738             if (t.active) {
17739                 r = t;
17740                 return false;
17741             }
17742             return null;
17743         });
17744         return r;
17745         
17746     },
17747     getPanelByName : function(n)
17748     {
17749         var r = false;
17750         Roo.each(this.tabs, function(t) {
17751             if (t.tabId == n) {
17752                 r = t;
17753                 return false;
17754             }
17755             return null;
17756         });
17757         return r;
17758     },
17759     indexOfPanel : function(p)
17760     {
17761         var r = false;
17762         Roo.each(this.tabs, function(t,i) {
17763             if (t.tabId == p.tabId) {
17764                 r = i;
17765                 return false;
17766             }
17767             return null;
17768         });
17769         return r;
17770     },
17771     /**
17772      * show a specific panel
17773      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17774      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17775      */
17776     showPanel : function (pan)
17777     {
17778         if(this.transition || typeof(pan) == 'undefined'){
17779             Roo.log("waiting for the transitionend");
17780             return;
17781         }
17782         
17783         if (typeof(pan) == 'number') {
17784             pan = this.tabs[pan];
17785         }
17786         
17787         if (typeof(pan) == 'string') {
17788             pan = this.getPanelByName(pan);
17789         }
17790         
17791         var cur = this.getActivePanel();
17792         
17793         if(!pan || !cur){
17794             Roo.log('pan or acitve pan is undefined');
17795             return false;
17796         }
17797         
17798         if (pan.tabId == this.getActivePanel().tabId) {
17799             return true;
17800         }
17801         
17802         if (false === cur.fireEvent('beforedeactivate')) {
17803             return false;
17804         }
17805         
17806         if(this.bullets > 0 && !Roo.isTouch){
17807             this.setActiveBullet(this.indexOfPanel(pan));
17808         }
17809         
17810         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17811             
17812             this.transition = true;
17813             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17814             var lr = dir == 'next' ? 'left' : 'right';
17815             pan.el.addClass(dir); // or prev
17816             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17817             cur.el.addClass(lr); // or right
17818             pan.el.addClass(lr);
17819             
17820             var _this = this;
17821             cur.el.on('transitionend', function() {
17822                 Roo.log("trans end?");
17823                 
17824                 pan.el.removeClass([lr,dir]);
17825                 pan.setActive(true);
17826                 
17827                 cur.el.removeClass([lr]);
17828                 cur.setActive(false);
17829                 
17830                 _this.transition = false;
17831                 
17832             }, this, { single:  true } );
17833             
17834             return true;
17835         }
17836         
17837         cur.setActive(false);
17838         pan.setActive(true);
17839         
17840         return true;
17841         
17842     },
17843     showPanelNext : function()
17844     {
17845         var i = this.indexOfPanel(this.getActivePanel());
17846         
17847         if (i >= this.tabs.length - 1 && !this.autoslide) {
17848             return;
17849         }
17850         
17851         if (i >= this.tabs.length - 1 && this.autoslide) {
17852             i = -1;
17853         }
17854         
17855         this.showPanel(this.tabs[i+1]);
17856     },
17857     
17858     showPanelPrev : function()
17859     {
17860         var i = this.indexOfPanel(this.getActivePanel());
17861         
17862         if (i  < 1 && !this.autoslide) {
17863             return;
17864         }
17865         
17866         if (i < 1 && this.autoslide) {
17867             i = this.tabs.length;
17868         }
17869         
17870         this.showPanel(this.tabs[i-1]);
17871     },
17872     
17873     
17874     addBullet: function()
17875     {
17876         if(!this.bullets || Roo.isTouch){
17877             return;
17878         }
17879         var ctr = this.el.select('.carousel-bullets',true).first();
17880         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17881         var bullet = ctr.createChild({
17882             cls : 'bullet bullet-' + i
17883         },ctr.dom.lastChild);
17884         
17885         
17886         var _this = this;
17887         
17888         bullet.on('click', (function(e, el, o, ii, t){
17889
17890             e.preventDefault();
17891
17892             this.showPanel(ii);
17893
17894             if(this.autoslide && this.slideFn){
17895                 clearInterval(this.slideFn);
17896                 this.slideFn = window.setInterval(function() {
17897                     _this.showPanelNext();
17898                 }, this.timer);
17899             }
17900
17901         }).createDelegate(this, [i, bullet], true));
17902                 
17903         
17904     },
17905      
17906     setActiveBullet : function(i)
17907     {
17908         if(Roo.isTouch){
17909             return;
17910         }
17911         
17912         Roo.each(this.el.select('.bullet', true).elements, function(el){
17913             el.removeClass('selected');
17914         });
17915
17916         var bullet = this.el.select('.bullet-' + i, true).first();
17917         
17918         if(!bullet){
17919             return;
17920         }
17921         
17922         bullet.addClass('selected');
17923     }
17924     
17925     
17926   
17927 });
17928
17929  
17930
17931  
17932  
17933 Roo.apply(Roo.bootstrap.TabGroup, {
17934     
17935     groups: {},
17936      /**
17937     * register a Navigation Group
17938     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17939     */
17940     register : function(navgrp)
17941     {
17942         this.groups[navgrp.navId] = navgrp;
17943         
17944     },
17945     /**
17946     * fetch a Navigation Group based on the navigation ID
17947     * if one does not exist , it will get created.
17948     * @param {string} the navgroup to add
17949     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17950     */
17951     get: function(navId) {
17952         if (typeof(this.groups[navId]) == 'undefined') {
17953             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17954         }
17955         return this.groups[navId] ;
17956     }
17957     
17958     
17959     
17960 });
17961
17962  /*
17963  * - LGPL
17964  *
17965  * TabPanel
17966  * 
17967  */
17968
17969 /**
17970  * @class Roo.bootstrap.TabPanel
17971  * @extends Roo.bootstrap.Component
17972  * Bootstrap TabPanel class
17973  * @cfg {Boolean} active panel active
17974  * @cfg {String} html panel content
17975  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17976  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17977  * @cfg {String} href click to link..
17978  * 
17979  * 
17980  * @constructor
17981  * Create a new TabPanel
17982  * @param {Object} config The config object
17983  */
17984
17985 Roo.bootstrap.TabPanel = function(config){
17986     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17987     this.addEvents({
17988         /**
17989              * @event changed
17990              * Fires when the active status changes
17991              * @param {Roo.bootstrap.TabPanel} this
17992              * @param {Boolean} state the new state
17993             
17994          */
17995         'changed': true,
17996         /**
17997              * @event beforedeactivate
17998              * Fires before a tab is de-activated - can be used to do validation on a form.
17999              * @param {Roo.bootstrap.TabPanel} this
18000              * @return {Boolean} false if there is an error
18001             
18002          */
18003         'beforedeactivate': true
18004      });
18005     
18006     this.tabId = this.tabId || Roo.id();
18007   
18008 };
18009
18010 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18011     
18012     active: false,
18013     html: false,
18014     tabId: false,
18015     navId : false,
18016     href : '',
18017     
18018     getAutoCreate : function(){
18019         var cfg = {
18020             tag: 'div',
18021             // item is needed for carousel - not sure if it has any effect otherwise
18022             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18023             html: this.html || ''
18024         };
18025         
18026         if(this.active){
18027             cfg.cls += ' active';
18028         }
18029         
18030         if(this.tabId){
18031             cfg.tabId = this.tabId;
18032         }
18033         
18034         
18035         return cfg;
18036     },
18037     
18038     initEvents:  function()
18039     {
18040         var p = this.parent();
18041         
18042         this.navId = this.navId || p.navId;
18043         
18044         if (typeof(this.navId) != 'undefined') {
18045             // not really needed.. but just in case.. parent should be a NavGroup.
18046             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18047             
18048             tg.register(this);
18049             
18050             var i = tg.tabs.length - 1;
18051             
18052             if(this.active && tg.bullets > 0 && i < tg.bullets){
18053                 tg.setActiveBullet(i);
18054             }
18055         }
18056         
18057         this.el.on('click', this.onClick, this);
18058         
18059         if(Roo.isTouch){
18060             this.el.on("touchstart", this.onTouchStart, this);
18061             this.el.on("touchmove", this.onTouchMove, this);
18062             this.el.on("touchend", this.onTouchEnd, this);
18063         }
18064         
18065     },
18066     
18067     onRender : function(ct, position)
18068     {
18069         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18070     },
18071     
18072     setActive : function(state)
18073     {
18074         Roo.log("panel - set active " + this.tabId + "=" + state);
18075         
18076         this.active = state;
18077         if (!state) {
18078             this.el.removeClass('active');
18079             
18080         } else  if (!this.el.hasClass('active')) {
18081             this.el.addClass('active');
18082         }
18083         
18084         this.fireEvent('changed', this, state);
18085     },
18086     
18087     onClick : function(e)
18088     {
18089         e.preventDefault();
18090         
18091         if(!this.href.length){
18092             return;
18093         }
18094         
18095         window.location.href = this.href;
18096     },
18097     
18098     startX : 0,
18099     startY : 0,
18100     endX : 0,
18101     endY : 0,
18102     swiping : false,
18103     
18104     onTouchStart : function(e)
18105     {
18106         this.swiping = false;
18107         
18108         this.startX = e.browserEvent.touches[0].clientX;
18109         this.startY = e.browserEvent.touches[0].clientY;
18110     },
18111     
18112     onTouchMove : function(e)
18113     {
18114         this.swiping = true;
18115         
18116         this.endX = e.browserEvent.touches[0].clientX;
18117         this.endY = e.browserEvent.touches[0].clientY;
18118     },
18119     
18120     onTouchEnd : function(e)
18121     {
18122         if(!this.swiping){
18123             this.onClick(e);
18124             return;
18125         }
18126         
18127         var tabGroup = this.parent();
18128         
18129         if(this.endX > this.startX){ // swiping right
18130             tabGroup.showPanelPrev();
18131             return;
18132         }
18133         
18134         if(this.startX > this.endX){ // swiping left
18135             tabGroup.showPanelNext();
18136             return;
18137         }
18138     }
18139     
18140     
18141 });
18142  
18143
18144  
18145
18146  /*
18147  * - LGPL
18148  *
18149  * DateField
18150  * 
18151  */
18152
18153 /**
18154  * @class Roo.bootstrap.DateField
18155  * @extends Roo.bootstrap.Input
18156  * Bootstrap DateField class
18157  * @cfg {Number} weekStart default 0
18158  * @cfg {String} viewMode default empty, (months|years)
18159  * @cfg {String} minViewMode default empty, (months|years)
18160  * @cfg {Number} startDate default -Infinity
18161  * @cfg {Number} endDate default Infinity
18162  * @cfg {Boolean} todayHighlight default false
18163  * @cfg {Boolean} todayBtn default false
18164  * @cfg {Boolean} calendarWeeks default false
18165  * @cfg {Object} daysOfWeekDisabled default empty
18166  * @cfg {Boolean} singleMode default false (true | false)
18167  * 
18168  * @cfg {Boolean} keyboardNavigation default true
18169  * @cfg {String} language default en
18170  * 
18171  * @constructor
18172  * Create a new DateField
18173  * @param {Object} config The config object
18174  */
18175
18176 Roo.bootstrap.DateField = function(config){
18177     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18178      this.addEvents({
18179             /**
18180              * @event show
18181              * Fires when this field show.
18182              * @param {Roo.bootstrap.DateField} this
18183              * @param {Mixed} date The date value
18184              */
18185             show : true,
18186             /**
18187              * @event show
18188              * Fires when this field hide.
18189              * @param {Roo.bootstrap.DateField} this
18190              * @param {Mixed} date The date value
18191              */
18192             hide : true,
18193             /**
18194              * @event select
18195              * Fires when select a date.
18196              * @param {Roo.bootstrap.DateField} this
18197              * @param {Mixed} date The date value
18198              */
18199             select : true,
18200             /**
18201              * @event beforeselect
18202              * Fires when before select a date.
18203              * @param {Roo.bootstrap.DateField} this
18204              * @param {Mixed} date The date value
18205              */
18206             beforeselect : true
18207         });
18208 };
18209
18210 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18211     
18212     /**
18213      * @cfg {String} format
18214      * The default date format string which can be overriden for localization support.  The format must be
18215      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18216      */
18217     format : "m/d/y",
18218     /**
18219      * @cfg {String} altFormats
18220      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18221      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18222      */
18223     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18224     
18225     weekStart : 0,
18226     
18227     viewMode : '',
18228     
18229     minViewMode : '',
18230     
18231     todayHighlight : false,
18232     
18233     todayBtn: false,
18234     
18235     language: 'en',
18236     
18237     keyboardNavigation: true,
18238     
18239     calendarWeeks: false,
18240     
18241     startDate: -Infinity,
18242     
18243     endDate: Infinity,
18244     
18245     daysOfWeekDisabled: [],
18246     
18247     _events: [],
18248     
18249     singleMode : false,
18250     
18251     UTCDate: function()
18252     {
18253         return new Date(Date.UTC.apply(Date, arguments));
18254     },
18255     
18256     UTCToday: function()
18257     {
18258         var today = new Date();
18259         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18260     },
18261     
18262     getDate: function() {
18263             var d = this.getUTCDate();
18264             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18265     },
18266     
18267     getUTCDate: function() {
18268             return this.date;
18269     },
18270     
18271     setDate: function(d) {
18272             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18273     },
18274     
18275     setUTCDate: function(d) {
18276             this.date = d;
18277             this.setValue(this.formatDate(this.date));
18278     },
18279         
18280     onRender: function(ct, position)
18281     {
18282         
18283         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18284         
18285         this.language = this.language || 'en';
18286         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18287         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18288         
18289         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18290         this.format = this.format || 'm/d/y';
18291         this.isInline = false;
18292         this.isInput = true;
18293         this.component = this.el.select('.add-on', true).first() || false;
18294         this.component = (this.component && this.component.length === 0) ? false : this.component;
18295         this.hasInput = this.component && this.inputEl().length;
18296         
18297         if (typeof(this.minViewMode === 'string')) {
18298             switch (this.minViewMode) {
18299                 case 'months':
18300                     this.minViewMode = 1;
18301                     break;
18302                 case 'years':
18303                     this.minViewMode = 2;
18304                     break;
18305                 default:
18306                     this.minViewMode = 0;
18307                     break;
18308             }
18309         }
18310         
18311         if (typeof(this.viewMode === 'string')) {
18312             switch (this.viewMode) {
18313                 case 'months':
18314                     this.viewMode = 1;
18315                     break;
18316                 case 'years':
18317                     this.viewMode = 2;
18318                     break;
18319                 default:
18320                     this.viewMode = 0;
18321                     break;
18322             }
18323         }
18324                 
18325         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18326         
18327 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18328         
18329         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18330         
18331         this.picker().on('mousedown', this.onMousedown, this);
18332         this.picker().on('click', this.onClick, this);
18333         
18334         this.picker().addClass('datepicker-dropdown');
18335         
18336         this.startViewMode = this.viewMode;
18337         
18338         if(this.singleMode){
18339             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18340                 v.setVisibilityMode(Roo.Element.DISPLAY);
18341                 v.hide();
18342             });
18343             
18344             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18345                 v.setStyle('width', '189px');
18346             });
18347         }
18348         
18349         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18350             if(!this.calendarWeeks){
18351                 v.remove();
18352                 return;
18353             }
18354             
18355             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18356             v.attr('colspan', function(i, val){
18357                 return parseInt(val) + 1;
18358             });
18359         });
18360                         
18361         
18362         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18363         
18364         this.setStartDate(this.startDate);
18365         this.setEndDate(this.endDate);
18366         
18367         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18368         
18369         this.fillDow();
18370         this.fillMonths();
18371         this.update();
18372         this.showMode();
18373         
18374         if(this.isInline) {
18375             this.show();
18376         }
18377     },
18378     
18379     picker : function()
18380     {
18381         return this.pickerEl;
18382 //        return this.el.select('.datepicker', true).first();
18383     },
18384     
18385     fillDow: function()
18386     {
18387         var dowCnt = this.weekStart;
18388         
18389         var dow = {
18390             tag: 'tr',
18391             cn: [
18392                 
18393             ]
18394         };
18395         
18396         if(this.calendarWeeks){
18397             dow.cn.push({
18398                 tag: 'th',
18399                 cls: 'cw',
18400                 html: '&nbsp;'
18401             })
18402         }
18403         
18404         while (dowCnt < this.weekStart + 7) {
18405             dow.cn.push({
18406                 tag: 'th',
18407                 cls: 'dow',
18408                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18409             });
18410         }
18411         
18412         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18413     },
18414     
18415     fillMonths: function()
18416     {    
18417         var i = 0;
18418         var months = this.picker().select('>.datepicker-months td', true).first();
18419         
18420         months.dom.innerHTML = '';
18421         
18422         while (i < 12) {
18423             var month = {
18424                 tag: 'span',
18425                 cls: 'month',
18426                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18427             };
18428             
18429             months.createChild(month);
18430         }
18431         
18432     },
18433     
18434     update: function()
18435     {
18436         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;
18437         
18438         if (this.date < this.startDate) {
18439             this.viewDate = new Date(this.startDate);
18440         } else if (this.date > this.endDate) {
18441             this.viewDate = new Date(this.endDate);
18442         } else {
18443             this.viewDate = new Date(this.date);
18444         }
18445         
18446         this.fill();
18447     },
18448     
18449     fill: function() 
18450     {
18451         var d = new Date(this.viewDate),
18452                 year = d.getUTCFullYear(),
18453                 month = d.getUTCMonth(),
18454                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18455                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18456                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18457                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18458                 currentDate = this.date && this.date.valueOf(),
18459                 today = this.UTCToday();
18460         
18461         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18462         
18463 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18464         
18465 //        this.picker.select('>tfoot th.today').
18466 //                                              .text(dates[this.language].today)
18467 //                                              .toggle(this.todayBtn !== false);
18468     
18469         this.updateNavArrows();
18470         this.fillMonths();
18471                                                 
18472         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18473         
18474         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18475          
18476         prevMonth.setUTCDate(day);
18477         
18478         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18479         
18480         var nextMonth = new Date(prevMonth);
18481         
18482         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18483         
18484         nextMonth = nextMonth.valueOf();
18485         
18486         var fillMonths = false;
18487         
18488         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18489         
18490         while(prevMonth.valueOf() < nextMonth) {
18491             var clsName = '';
18492             
18493             if (prevMonth.getUTCDay() === this.weekStart) {
18494                 if(fillMonths){
18495                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18496                 }
18497                     
18498                 fillMonths = {
18499                     tag: 'tr',
18500                     cn: []
18501                 };
18502                 
18503                 if(this.calendarWeeks){
18504                     // ISO 8601: First week contains first thursday.
18505                     // ISO also states week starts on Monday, but we can be more abstract here.
18506                     var
18507                     // Start of current week: based on weekstart/current date
18508                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18509                     // Thursday of this week
18510                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18511                     // First Thursday of year, year from thursday
18512                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18513                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18514                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18515                     
18516                     fillMonths.cn.push({
18517                         tag: 'td',
18518                         cls: 'cw',
18519                         html: calWeek
18520                     });
18521                 }
18522             }
18523             
18524             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18525                 clsName += ' old';
18526             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18527                 clsName += ' new';
18528             }
18529             if (this.todayHighlight &&
18530                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18531                 prevMonth.getUTCMonth() == today.getMonth() &&
18532                 prevMonth.getUTCDate() == today.getDate()) {
18533                 clsName += ' today';
18534             }
18535             
18536             if (currentDate && prevMonth.valueOf() === currentDate) {
18537                 clsName += ' active';
18538             }
18539             
18540             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18541                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18542                     clsName += ' disabled';
18543             }
18544             
18545             fillMonths.cn.push({
18546                 tag: 'td',
18547                 cls: 'day ' + clsName,
18548                 html: prevMonth.getDate()
18549             });
18550             
18551             prevMonth.setDate(prevMonth.getDate()+1);
18552         }
18553           
18554         var currentYear = this.date && this.date.getUTCFullYear();
18555         var currentMonth = this.date && this.date.getUTCMonth();
18556         
18557         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18558         
18559         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18560             v.removeClass('active');
18561             
18562             if(currentYear === year && k === currentMonth){
18563                 v.addClass('active');
18564             }
18565             
18566             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18567                 v.addClass('disabled');
18568             }
18569             
18570         });
18571         
18572         
18573         year = parseInt(year/10, 10) * 10;
18574         
18575         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18576         
18577         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18578         
18579         year -= 1;
18580         for (var i = -1; i < 11; i++) {
18581             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18582                 tag: 'span',
18583                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18584                 html: year
18585             });
18586             
18587             year += 1;
18588         }
18589     },
18590     
18591     showMode: function(dir) 
18592     {
18593         if (dir) {
18594             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18595         }
18596         
18597         Roo.each(this.picker().select('>div',true).elements, function(v){
18598             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18599             v.hide();
18600         });
18601         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18602     },
18603     
18604     place: function()
18605     {
18606         if(this.isInline) {
18607             return;
18608         }
18609         
18610         this.picker().removeClass(['bottom', 'top']);
18611         
18612         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18613             /*
18614              * place to the top of element!
18615              *
18616              */
18617             
18618             this.picker().addClass('top');
18619             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18620             
18621             return;
18622         }
18623         
18624         this.picker().addClass('bottom');
18625         
18626         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18627     },
18628     
18629     parseDate : function(value)
18630     {
18631         if(!value || value instanceof Date){
18632             return value;
18633         }
18634         var v = Date.parseDate(value, this.format);
18635         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18636             v = Date.parseDate(value, 'Y-m-d');
18637         }
18638         if(!v && this.altFormats){
18639             if(!this.altFormatsArray){
18640                 this.altFormatsArray = this.altFormats.split("|");
18641             }
18642             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18643                 v = Date.parseDate(value, this.altFormatsArray[i]);
18644             }
18645         }
18646         return v;
18647     },
18648     
18649     formatDate : function(date, fmt)
18650     {   
18651         return (!date || !(date instanceof Date)) ?
18652         date : date.dateFormat(fmt || this.format);
18653     },
18654     
18655     onFocus : function()
18656     {
18657         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18658         this.show();
18659     },
18660     
18661     onBlur : function()
18662     {
18663         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18664         
18665         var d = this.inputEl().getValue();
18666         
18667         this.setValue(d);
18668                 
18669         this.hide();
18670     },
18671     
18672     show : function()
18673     {
18674         this.picker().show();
18675         this.update();
18676         this.place();
18677         
18678         this.fireEvent('show', this, this.date);
18679     },
18680     
18681     hide : function()
18682     {
18683         if(this.isInline) {
18684             return;
18685         }
18686         this.picker().hide();
18687         this.viewMode = this.startViewMode;
18688         this.showMode();
18689         
18690         this.fireEvent('hide', this, this.date);
18691         
18692     },
18693     
18694     onMousedown: function(e)
18695     {
18696         e.stopPropagation();
18697         e.preventDefault();
18698     },
18699     
18700     keyup: function(e)
18701     {
18702         Roo.bootstrap.DateField.superclass.keyup.call(this);
18703         this.update();
18704     },
18705
18706     setValue: function(v)
18707     {
18708         if(this.fireEvent('beforeselect', this, v) !== false){
18709             var d = new Date(this.parseDate(v) ).clearTime();
18710         
18711             if(isNaN(d.getTime())){
18712                 this.date = this.viewDate = '';
18713                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18714                 return;
18715             }
18716
18717             v = this.formatDate(d);
18718
18719             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18720
18721             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18722
18723             this.update();
18724
18725             this.fireEvent('select', this, this.date);
18726         }
18727     },
18728     
18729     getValue: function()
18730     {
18731         return this.formatDate(this.date);
18732     },
18733     
18734     fireKey: function(e)
18735     {
18736         if (!this.picker().isVisible()){
18737             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18738                 this.show();
18739             }
18740             return;
18741         }
18742         
18743         var dateChanged = false,
18744         dir, day, month,
18745         newDate, newViewDate;
18746         
18747         switch(e.keyCode){
18748             case 27: // escape
18749                 this.hide();
18750                 e.preventDefault();
18751                 break;
18752             case 37: // left
18753             case 39: // right
18754                 if (!this.keyboardNavigation) {
18755                     break;
18756                 }
18757                 dir = e.keyCode == 37 ? -1 : 1;
18758                 
18759                 if (e.ctrlKey){
18760                     newDate = this.moveYear(this.date, dir);
18761                     newViewDate = this.moveYear(this.viewDate, dir);
18762                 } else if (e.shiftKey){
18763                     newDate = this.moveMonth(this.date, dir);
18764                     newViewDate = this.moveMonth(this.viewDate, dir);
18765                 } else {
18766                     newDate = new Date(this.date);
18767                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18768                     newViewDate = new Date(this.viewDate);
18769                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18770                 }
18771                 if (this.dateWithinRange(newDate)){
18772                     this.date = newDate;
18773                     this.viewDate = newViewDate;
18774                     this.setValue(this.formatDate(this.date));
18775 //                    this.update();
18776                     e.preventDefault();
18777                     dateChanged = true;
18778                 }
18779                 break;
18780             case 38: // up
18781             case 40: // down
18782                 if (!this.keyboardNavigation) {
18783                     break;
18784                 }
18785                 dir = e.keyCode == 38 ? -1 : 1;
18786                 if (e.ctrlKey){
18787                     newDate = this.moveYear(this.date, dir);
18788                     newViewDate = this.moveYear(this.viewDate, dir);
18789                 } else if (e.shiftKey){
18790                     newDate = this.moveMonth(this.date, dir);
18791                     newViewDate = this.moveMonth(this.viewDate, dir);
18792                 } else {
18793                     newDate = new Date(this.date);
18794                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18795                     newViewDate = new Date(this.viewDate);
18796                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18797                 }
18798                 if (this.dateWithinRange(newDate)){
18799                     this.date = newDate;
18800                     this.viewDate = newViewDate;
18801                     this.setValue(this.formatDate(this.date));
18802 //                    this.update();
18803                     e.preventDefault();
18804                     dateChanged = true;
18805                 }
18806                 break;
18807             case 13: // enter
18808                 this.setValue(this.formatDate(this.date));
18809                 this.hide();
18810                 e.preventDefault();
18811                 break;
18812             case 9: // tab
18813                 this.setValue(this.formatDate(this.date));
18814                 this.hide();
18815                 break;
18816             case 16: // shift
18817             case 17: // ctrl
18818             case 18: // alt
18819                 break;
18820             default :
18821                 this.hide();
18822                 
18823         }
18824     },
18825     
18826     
18827     onClick: function(e) 
18828     {
18829         e.stopPropagation();
18830         e.preventDefault();
18831         
18832         var target = e.getTarget();
18833         
18834         if(target.nodeName.toLowerCase() === 'i'){
18835             target = Roo.get(target).dom.parentNode;
18836         }
18837         
18838         var nodeName = target.nodeName;
18839         var className = target.className;
18840         var html = target.innerHTML;
18841         //Roo.log(nodeName);
18842         
18843         switch(nodeName.toLowerCase()) {
18844             case 'th':
18845                 switch(className) {
18846                     case 'switch':
18847                         this.showMode(1);
18848                         break;
18849                     case 'prev':
18850                     case 'next':
18851                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18852                         switch(this.viewMode){
18853                                 case 0:
18854                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18855                                         break;
18856                                 case 1:
18857                                 case 2:
18858                                         this.viewDate = this.moveYear(this.viewDate, dir);
18859                                         break;
18860                         }
18861                         this.fill();
18862                         break;
18863                     case 'today':
18864                         var date = new Date();
18865                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18866 //                        this.fill()
18867                         this.setValue(this.formatDate(this.date));
18868                         
18869                         this.hide();
18870                         break;
18871                 }
18872                 break;
18873             case 'span':
18874                 if (className.indexOf('disabled') < 0) {
18875                     this.viewDate.setUTCDate(1);
18876                     if (className.indexOf('month') > -1) {
18877                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18878                     } else {
18879                         var year = parseInt(html, 10) || 0;
18880                         this.viewDate.setUTCFullYear(year);
18881                         
18882                     }
18883                     
18884                     if(this.singleMode){
18885                         this.setValue(this.formatDate(this.viewDate));
18886                         this.hide();
18887                         return;
18888                     }
18889                     
18890                     this.showMode(-1);
18891                     this.fill();
18892                 }
18893                 break;
18894                 
18895             case 'td':
18896                 //Roo.log(className);
18897                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18898                     var day = parseInt(html, 10) || 1;
18899                     var year = this.viewDate.getUTCFullYear(),
18900                         month = this.viewDate.getUTCMonth();
18901
18902                     if (className.indexOf('old') > -1) {
18903                         if(month === 0 ){
18904                             month = 11;
18905                             year -= 1;
18906                         }else{
18907                             month -= 1;
18908                         }
18909                     } else if (className.indexOf('new') > -1) {
18910                         if (month == 11) {
18911                             month = 0;
18912                             year += 1;
18913                         } else {
18914                             month += 1;
18915                         }
18916                     }
18917                     //Roo.log([year,month,day]);
18918                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18919                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18920 //                    this.fill();
18921                     //Roo.log(this.formatDate(this.date));
18922                     this.setValue(this.formatDate(this.date));
18923                     this.hide();
18924                 }
18925                 break;
18926         }
18927     },
18928     
18929     setStartDate: function(startDate)
18930     {
18931         this.startDate = startDate || -Infinity;
18932         if (this.startDate !== -Infinity) {
18933             this.startDate = this.parseDate(this.startDate);
18934         }
18935         this.update();
18936         this.updateNavArrows();
18937     },
18938
18939     setEndDate: function(endDate)
18940     {
18941         this.endDate = endDate || Infinity;
18942         if (this.endDate !== Infinity) {
18943             this.endDate = this.parseDate(this.endDate);
18944         }
18945         this.update();
18946         this.updateNavArrows();
18947     },
18948     
18949     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18950     {
18951         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18952         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18953             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18954         }
18955         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18956             return parseInt(d, 10);
18957         });
18958         this.update();
18959         this.updateNavArrows();
18960     },
18961     
18962     updateNavArrows: function() 
18963     {
18964         if(this.singleMode){
18965             return;
18966         }
18967         
18968         var d = new Date(this.viewDate),
18969         year = d.getUTCFullYear(),
18970         month = d.getUTCMonth();
18971         
18972         Roo.each(this.picker().select('.prev', true).elements, function(v){
18973             v.show();
18974             switch (this.viewMode) {
18975                 case 0:
18976
18977                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18978                         v.hide();
18979                     }
18980                     break;
18981                 case 1:
18982                 case 2:
18983                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18984                         v.hide();
18985                     }
18986                     break;
18987             }
18988         });
18989         
18990         Roo.each(this.picker().select('.next', true).elements, function(v){
18991             v.show();
18992             switch (this.viewMode) {
18993                 case 0:
18994
18995                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18996                         v.hide();
18997                     }
18998                     break;
18999                 case 1:
19000                 case 2:
19001                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19002                         v.hide();
19003                     }
19004                     break;
19005             }
19006         })
19007     },
19008     
19009     moveMonth: function(date, dir)
19010     {
19011         if (!dir) {
19012             return date;
19013         }
19014         var new_date = new Date(date.valueOf()),
19015         day = new_date.getUTCDate(),
19016         month = new_date.getUTCMonth(),
19017         mag = Math.abs(dir),
19018         new_month, test;
19019         dir = dir > 0 ? 1 : -1;
19020         if (mag == 1){
19021             test = dir == -1
19022             // If going back one month, make sure month is not current month
19023             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19024             ? function(){
19025                 return new_date.getUTCMonth() == month;
19026             }
19027             // If going forward one month, make sure month is as expected
19028             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19029             : function(){
19030                 return new_date.getUTCMonth() != new_month;
19031             };
19032             new_month = month + dir;
19033             new_date.setUTCMonth(new_month);
19034             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19035             if (new_month < 0 || new_month > 11) {
19036                 new_month = (new_month + 12) % 12;
19037             }
19038         } else {
19039             // For magnitudes >1, move one month at a time...
19040             for (var i=0; i<mag; i++) {
19041                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19042                 new_date = this.moveMonth(new_date, dir);
19043             }
19044             // ...then reset the day, keeping it in the new month
19045             new_month = new_date.getUTCMonth();
19046             new_date.setUTCDate(day);
19047             test = function(){
19048                 return new_month != new_date.getUTCMonth();
19049             };
19050         }
19051         // Common date-resetting loop -- if date is beyond end of month, make it
19052         // end of month
19053         while (test()){
19054             new_date.setUTCDate(--day);
19055             new_date.setUTCMonth(new_month);
19056         }
19057         return new_date;
19058     },
19059
19060     moveYear: function(date, dir)
19061     {
19062         return this.moveMonth(date, dir*12);
19063     },
19064
19065     dateWithinRange: function(date)
19066     {
19067         return date >= this.startDate && date <= this.endDate;
19068     },
19069
19070     
19071     remove: function() 
19072     {
19073         this.picker().remove();
19074     },
19075     
19076     validateValue : function(value)
19077     {
19078         if(value.length < 1)  {
19079             if(this.allowBlank){
19080                 return true;
19081             }
19082             return false;
19083         }
19084         
19085         if(value.length < this.minLength){
19086             return false;
19087         }
19088         if(value.length > this.maxLength){
19089             return false;
19090         }
19091         if(this.vtype){
19092             var vt = Roo.form.VTypes;
19093             if(!vt[this.vtype](value, this)){
19094                 return false;
19095             }
19096         }
19097         if(typeof this.validator == "function"){
19098             var msg = this.validator(value);
19099             if(msg !== true){
19100                 return false;
19101             }
19102         }
19103         
19104         if(this.regex && !this.regex.test(value)){
19105             return false;
19106         }
19107         
19108         if(typeof(this.parseDate(value)) == 'undefined'){
19109             return false;
19110         }
19111         
19112         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19113             return false;
19114         }      
19115         
19116         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19117             return false;
19118         } 
19119         
19120         
19121         return true;
19122     }
19123    
19124 });
19125
19126 Roo.apply(Roo.bootstrap.DateField,  {
19127     
19128     head : {
19129         tag: 'thead',
19130         cn: [
19131         {
19132             tag: 'tr',
19133             cn: [
19134             {
19135                 tag: 'th',
19136                 cls: 'prev',
19137                 html: '<i class="fa fa-arrow-left"/>'
19138             },
19139             {
19140                 tag: 'th',
19141                 cls: 'switch',
19142                 colspan: '5'
19143             },
19144             {
19145                 tag: 'th',
19146                 cls: 'next',
19147                 html: '<i class="fa fa-arrow-right"/>'
19148             }
19149
19150             ]
19151         }
19152         ]
19153     },
19154     
19155     content : {
19156         tag: 'tbody',
19157         cn: [
19158         {
19159             tag: 'tr',
19160             cn: [
19161             {
19162                 tag: 'td',
19163                 colspan: '7'
19164             }
19165             ]
19166         }
19167         ]
19168     },
19169     
19170     footer : {
19171         tag: 'tfoot',
19172         cn: [
19173         {
19174             tag: 'tr',
19175             cn: [
19176             {
19177                 tag: 'th',
19178                 colspan: '7',
19179                 cls: 'today'
19180             }
19181                     
19182             ]
19183         }
19184         ]
19185     },
19186     
19187     dates:{
19188         en: {
19189             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19190             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19191             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19192             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19193             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19194             today: "Today"
19195         }
19196     },
19197     
19198     modes: [
19199     {
19200         clsName: 'days',
19201         navFnc: 'Month',
19202         navStep: 1
19203     },
19204     {
19205         clsName: 'months',
19206         navFnc: 'FullYear',
19207         navStep: 1
19208     },
19209     {
19210         clsName: 'years',
19211         navFnc: 'FullYear',
19212         navStep: 10
19213     }]
19214 });
19215
19216 Roo.apply(Roo.bootstrap.DateField,  {
19217   
19218     template : {
19219         tag: 'div',
19220         cls: 'datepicker dropdown-menu roo-dynamic',
19221         cn: [
19222         {
19223             tag: 'div',
19224             cls: 'datepicker-days',
19225             cn: [
19226             {
19227                 tag: 'table',
19228                 cls: 'table-condensed',
19229                 cn:[
19230                 Roo.bootstrap.DateField.head,
19231                 {
19232                     tag: 'tbody'
19233                 },
19234                 Roo.bootstrap.DateField.footer
19235                 ]
19236             }
19237             ]
19238         },
19239         {
19240             tag: 'div',
19241             cls: 'datepicker-months',
19242             cn: [
19243             {
19244                 tag: 'table',
19245                 cls: 'table-condensed',
19246                 cn:[
19247                 Roo.bootstrap.DateField.head,
19248                 Roo.bootstrap.DateField.content,
19249                 Roo.bootstrap.DateField.footer
19250                 ]
19251             }
19252             ]
19253         },
19254         {
19255             tag: 'div',
19256             cls: 'datepicker-years',
19257             cn: [
19258             {
19259                 tag: 'table',
19260                 cls: 'table-condensed',
19261                 cn:[
19262                 Roo.bootstrap.DateField.head,
19263                 Roo.bootstrap.DateField.content,
19264                 Roo.bootstrap.DateField.footer
19265                 ]
19266             }
19267             ]
19268         }
19269         ]
19270     }
19271 });
19272
19273  
19274
19275  /*
19276  * - LGPL
19277  *
19278  * TimeField
19279  * 
19280  */
19281
19282 /**
19283  * @class Roo.bootstrap.TimeField
19284  * @extends Roo.bootstrap.Input
19285  * Bootstrap DateField class
19286  * 
19287  * 
19288  * @constructor
19289  * Create a new TimeField
19290  * @param {Object} config The config object
19291  */
19292
19293 Roo.bootstrap.TimeField = function(config){
19294     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19295     this.addEvents({
19296             /**
19297              * @event show
19298              * Fires when this field show.
19299              * @param {Roo.bootstrap.DateField} thisthis
19300              * @param {Mixed} date The date value
19301              */
19302             show : true,
19303             /**
19304              * @event show
19305              * Fires when this field hide.
19306              * @param {Roo.bootstrap.DateField} this
19307              * @param {Mixed} date The date value
19308              */
19309             hide : true,
19310             /**
19311              * @event select
19312              * Fires when select a date.
19313              * @param {Roo.bootstrap.DateField} this
19314              * @param {Mixed} date The date value
19315              */
19316             select : true
19317         });
19318 };
19319
19320 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19321     
19322     /**
19323      * @cfg {String} format
19324      * The default time format string which can be overriden for localization support.  The format must be
19325      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19326      */
19327     format : "H:i",
19328        
19329     onRender: function(ct, position)
19330     {
19331         
19332         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19333                 
19334         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19335         
19336         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337         
19338         this.pop = this.picker().select('>.datepicker-time',true).first();
19339         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340         
19341         this.picker().on('mousedown', this.onMousedown, this);
19342         this.picker().on('click', this.onClick, this);
19343         
19344         this.picker().addClass('datepicker-dropdown');
19345     
19346         this.fillTime();
19347         this.update();
19348             
19349         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19350         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19351         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19352         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19353         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19354         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19355
19356     },
19357     
19358     fireKey: function(e){
19359         if (!this.picker().isVisible()){
19360             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19361                 this.show();
19362             }
19363             return;
19364         }
19365
19366         e.preventDefault();
19367         
19368         switch(e.keyCode){
19369             case 27: // escape
19370                 this.hide();
19371                 break;
19372             case 37: // left
19373             case 39: // right
19374                 this.onTogglePeriod();
19375                 break;
19376             case 38: // up
19377                 this.onIncrementMinutes();
19378                 break;
19379             case 40: // down
19380                 this.onDecrementMinutes();
19381                 break;
19382             case 13: // enter
19383             case 9: // tab
19384                 this.setTime();
19385                 break;
19386         }
19387     },
19388     
19389     onClick: function(e) {
19390         e.stopPropagation();
19391         e.preventDefault();
19392     },
19393     
19394     picker : function()
19395     {
19396         return this.el.select('.datepicker', true).first();
19397     },
19398     
19399     fillTime: function()
19400     {    
19401         var time = this.pop.select('tbody', true).first();
19402         
19403         time.dom.innerHTML = '';
19404         
19405         time.createChild({
19406             tag: 'tr',
19407             cn: [
19408                 {
19409                     tag: 'td',
19410                     cn: [
19411                         {
19412                             tag: 'a',
19413                             href: '#',
19414                             cls: 'btn',
19415                             cn: [
19416                                 {
19417                                     tag: 'span',
19418                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19419                                 }
19420                             ]
19421                         } 
19422                     ]
19423                 },
19424                 {
19425                     tag: 'td',
19426                     cls: 'separator'
19427                 },
19428                 {
19429                     tag: 'td',
19430                     cn: [
19431                         {
19432                             tag: 'a',
19433                             href: '#',
19434                             cls: 'btn',
19435                             cn: [
19436                                 {
19437                                     tag: 'span',
19438                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19439                                 }
19440                             ]
19441                         }
19442                     ]
19443                 },
19444                 {
19445                     tag: 'td',
19446                     cls: 'separator'
19447                 }
19448             ]
19449         });
19450         
19451         time.createChild({
19452             tag: 'tr',
19453             cn: [
19454                 {
19455                     tag: 'td',
19456                     cn: [
19457                         {
19458                             tag: 'span',
19459                             cls: 'timepicker-hour',
19460                             html: '00'
19461                         }  
19462                     ]
19463                 },
19464                 {
19465                     tag: 'td',
19466                     cls: 'separator',
19467                     html: ':'
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cn: [
19472                         {
19473                             tag: 'span',
19474                             cls: 'timepicker-minute',
19475                             html: '00'
19476                         }  
19477                     ]
19478                 },
19479                 {
19480                     tag: 'td',
19481                     cls: 'separator'
19482                 },
19483                 {
19484                     tag: 'td',
19485                     cn: [
19486                         {
19487                             tag: 'button',
19488                             type: 'button',
19489                             cls: 'btn btn-primary period',
19490                             html: 'AM'
19491                             
19492                         }
19493                     ]
19494                 }
19495             ]
19496         });
19497         
19498         time.createChild({
19499             tag: 'tr',
19500             cn: [
19501                 {
19502                     tag: 'td',
19503                     cn: [
19504                         {
19505                             tag: 'a',
19506                             href: '#',
19507                             cls: 'btn',
19508                             cn: [
19509                                 {
19510                                     tag: 'span',
19511                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19512                                 }
19513                             ]
19514                         }
19515                     ]
19516                 },
19517                 {
19518                     tag: 'td',
19519                     cls: 'separator'
19520                 },
19521                 {
19522                     tag: 'td',
19523                     cn: [
19524                         {
19525                             tag: 'a',
19526                             href: '#',
19527                             cls: 'btn',
19528                             cn: [
19529                                 {
19530                                     tag: 'span',
19531                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19532                                 }
19533                             ]
19534                         }
19535                     ]
19536                 },
19537                 {
19538                     tag: 'td',
19539                     cls: 'separator'
19540                 }
19541             ]
19542         });
19543         
19544     },
19545     
19546     update: function()
19547     {
19548         
19549         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19550         
19551         this.fill();
19552     },
19553     
19554     fill: function() 
19555     {
19556         var hours = this.time.getHours();
19557         var minutes = this.time.getMinutes();
19558         var period = 'AM';
19559         
19560         if(hours > 11){
19561             period = 'PM';
19562         }
19563         
19564         if(hours == 0){
19565             hours = 12;
19566         }
19567         
19568         
19569         if(hours > 12){
19570             hours = hours - 12;
19571         }
19572         
19573         if(hours < 10){
19574             hours = '0' + hours;
19575         }
19576         
19577         if(minutes < 10){
19578             minutes = '0' + minutes;
19579         }
19580         
19581         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19582         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19583         this.pop.select('button', true).first().dom.innerHTML = period;
19584         
19585     },
19586     
19587     place: function()
19588     {   
19589         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19590         
19591         var cls = ['bottom'];
19592         
19593         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19594             cls.pop();
19595             cls.push('top');
19596         }
19597         
19598         cls.push('right');
19599         
19600         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19601             cls.pop();
19602             cls.push('left');
19603         }
19604         
19605         this.picker().addClass(cls.join('-'));
19606         
19607         var _this = this;
19608         
19609         Roo.each(cls, function(c){
19610             if(c == 'bottom'){
19611                 _this.picker().setTop(_this.inputEl().getHeight());
19612                 return;
19613             }
19614             if(c == 'top'){
19615                 _this.picker().setTop(0 - _this.picker().getHeight());
19616                 return;
19617             }
19618             
19619             if(c == 'left'){
19620                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19621                 return;
19622             }
19623             if(c == 'right'){
19624                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19625                 return;
19626             }
19627         });
19628         
19629     },
19630   
19631     onFocus : function()
19632     {
19633         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19634         this.show();
19635     },
19636     
19637     onBlur : function()
19638     {
19639         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19640         this.hide();
19641     },
19642     
19643     show : function()
19644     {
19645         this.picker().show();
19646         this.pop.show();
19647         this.update();
19648         this.place();
19649         
19650         this.fireEvent('show', this, this.date);
19651     },
19652     
19653     hide : function()
19654     {
19655         this.picker().hide();
19656         this.pop.hide();
19657         
19658         this.fireEvent('hide', this, this.date);
19659     },
19660     
19661     setTime : function()
19662     {
19663         this.hide();
19664         this.setValue(this.time.format(this.format));
19665         
19666         this.fireEvent('select', this, this.date);
19667         
19668         
19669     },
19670     
19671     onMousedown: function(e){
19672         e.stopPropagation();
19673         e.preventDefault();
19674     },
19675     
19676     onIncrementHours: function()
19677     {
19678         Roo.log('onIncrementHours');
19679         this.time = this.time.add(Date.HOUR, 1);
19680         this.update();
19681         
19682     },
19683     
19684     onDecrementHours: function()
19685     {
19686         Roo.log('onDecrementHours');
19687         this.time = this.time.add(Date.HOUR, -1);
19688         this.update();
19689     },
19690     
19691     onIncrementMinutes: function()
19692     {
19693         Roo.log('onIncrementMinutes');
19694         this.time = this.time.add(Date.MINUTE, 1);
19695         this.update();
19696     },
19697     
19698     onDecrementMinutes: function()
19699     {
19700         Roo.log('onDecrementMinutes');
19701         this.time = this.time.add(Date.MINUTE, -1);
19702         this.update();
19703     },
19704     
19705     onTogglePeriod: function()
19706     {
19707         Roo.log('onTogglePeriod');
19708         this.time = this.time.add(Date.HOUR, 12);
19709         this.update();
19710     }
19711     
19712    
19713 });
19714
19715 Roo.apply(Roo.bootstrap.TimeField,  {
19716     
19717     content : {
19718         tag: 'tbody',
19719         cn: [
19720             {
19721                 tag: 'tr',
19722                 cn: [
19723                 {
19724                     tag: 'td',
19725                     colspan: '7'
19726                 }
19727                 ]
19728             }
19729         ]
19730     },
19731     
19732     footer : {
19733         tag: 'tfoot',
19734         cn: [
19735             {
19736                 tag: 'tr',
19737                 cn: [
19738                 {
19739                     tag: 'th',
19740                     colspan: '7',
19741                     cls: '',
19742                     cn: [
19743                         {
19744                             tag: 'button',
19745                             cls: 'btn btn-info ok',
19746                             html: 'OK'
19747                         }
19748                     ]
19749                 }
19750
19751                 ]
19752             }
19753         ]
19754     }
19755 });
19756
19757 Roo.apply(Roo.bootstrap.TimeField,  {
19758   
19759     template : {
19760         tag: 'div',
19761         cls: 'datepicker dropdown-menu',
19762         cn: [
19763             {
19764                 tag: 'div',
19765                 cls: 'datepicker-time',
19766                 cn: [
19767                 {
19768                     tag: 'table',
19769                     cls: 'table-condensed',
19770                     cn:[
19771                     Roo.bootstrap.TimeField.content,
19772                     Roo.bootstrap.TimeField.footer
19773                     ]
19774                 }
19775                 ]
19776             }
19777         ]
19778     }
19779 });
19780
19781  
19782
19783  /*
19784  * - LGPL
19785  *
19786  * MonthField
19787  * 
19788  */
19789
19790 /**
19791  * @class Roo.bootstrap.MonthField
19792  * @extends Roo.bootstrap.Input
19793  * Bootstrap MonthField class
19794  * 
19795  * @cfg {String} language default en
19796  * 
19797  * @constructor
19798  * Create a new MonthField
19799  * @param {Object} config The config object
19800  */
19801
19802 Roo.bootstrap.MonthField = function(config){
19803     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19804     
19805     this.addEvents({
19806         /**
19807          * @event show
19808          * Fires when this field show.
19809          * @param {Roo.bootstrap.MonthField} this
19810          * @param {Mixed} date The date value
19811          */
19812         show : true,
19813         /**
19814          * @event show
19815          * Fires when this field hide.
19816          * @param {Roo.bootstrap.MonthField} this
19817          * @param {Mixed} date The date value
19818          */
19819         hide : true,
19820         /**
19821          * @event select
19822          * Fires when select a date.
19823          * @param {Roo.bootstrap.MonthField} this
19824          * @param {String} oldvalue The old value
19825          * @param {String} newvalue The new value
19826          */
19827         select : true
19828     });
19829 };
19830
19831 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19832     
19833     onRender: function(ct, position)
19834     {
19835         
19836         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19837         
19838         this.language = this.language || 'en';
19839         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19840         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19841         
19842         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19843         this.isInline = false;
19844         this.isInput = true;
19845         this.component = this.el.select('.add-on', true).first() || false;
19846         this.component = (this.component && this.component.length === 0) ? false : this.component;
19847         this.hasInput = this.component && this.inputEL().length;
19848         
19849         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19850         
19851         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19852         
19853         this.picker().on('mousedown', this.onMousedown, this);
19854         this.picker().on('click', this.onClick, this);
19855         
19856         this.picker().addClass('datepicker-dropdown');
19857         
19858         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19859             v.setStyle('width', '189px');
19860         });
19861         
19862         this.fillMonths();
19863         
19864         this.update();
19865         
19866         if(this.isInline) {
19867             this.show();
19868         }
19869         
19870     },
19871     
19872     setValue: function(v, suppressEvent)
19873     {   
19874         var o = this.getValue();
19875         
19876         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19877         
19878         this.update();
19879
19880         if(suppressEvent !== true){
19881             this.fireEvent('select', this, o, v);
19882         }
19883         
19884     },
19885     
19886     getValue: function()
19887     {
19888         return this.value;
19889     },
19890     
19891     onClick: function(e) 
19892     {
19893         e.stopPropagation();
19894         e.preventDefault();
19895         
19896         var target = e.getTarget();
19897         
19898         if(target.nodeName.toLowerCase() === 'i'){
19899             target = Roo.get(target).dom.parentNode;
19900         }
19901         
19902         var nodeName = target.nodeName;
19903         var className = target.className;
19904         var html = target.innerHTML;
19905         
19906         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19907             return;
19908         }
19909         
19910         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19911         
19912         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19913         
19914         this.hide();
19915                         
19916     },
19917     
19918     picker : function()
19919     {
19920         return this.pickerEl;
19921     },
19922     
19923     fillMonths: function()
19924     {    
19925         var i = 0;
19926         var months = this.picker().select('>.datepicker-months td', true).first();
19927         
19928         months.dom.innerHTML = '';
19929         
19930         while (i < 12) {
19931             var month = {
19932                 tag: 'span',
19933                 cls: 'month',
19934                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19935             };
19936             
19937             months.createChild(month);
19938         }
19939         
19940     },
19941     
19942     update: function()
19943     {
19944         var _this = this;
19945         
19946         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19947             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19948         }
19949         
19950         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19951             e.removeClass('active');
19952             
19953             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19954                 e.addClass('active');
19955             }
19956         })
19957     },
19958     
19959     place: function()
19960     {
19961         if(this.isInline) {
19962             return;
19963         }
19964         
19965         this.picker().removeClass(['bottom', 'top']);
19966         
19967         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19968             /*
19969              * place to the top of element!
19970              *
19971              */
19972             
19973             this.picker().addClass('top');
19974             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19975             
19976             return;
19977         }
19978         
19979         this.picker().addClass('bottom');
19980         
19981         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19982     },
19983     
19984     onFocus : function()
19985     {
19986         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19987         this.show();
19988     },
19989     
19990     onBlur : function()
19991     {
19992         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19993         
19994         var d = this.inputEl().getValue();
19995         
19996         this.setValue(d);
19997                 
19998         this.hide();
19999     },
20000     
20001     show : function()
20002     {
20003         this.picker().show();
20004         this.picker().select('>.datepicker-months', true).first().show();
20005         this.update();
20006         this.place();
20007         
20008         this.fireEvent('show', this, this.date);
20009     },
20010     
20011     hide : function()
20012     {
20013         if(this.isInline) {
20014             return;
20015         }
20016         this.picker().hide();
20017         this.fireEvent('hide', this, this.date);
20018         
20019     },
20020     
20021     onMousedown: function(e)
20022     {
20023         e.stopPropagation();
20024         e.preventDefault();
20025     },
20026     
20027     keyup: function(e)
20028     {
20029         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20030         this.update();
20031     },
20032
20033     fireKey: function(e)
20034     {
20035         if (!this.picker().isVisible()){
20036             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20037                 this.show();
20038             }
20039             return;
20040         }
20041         
20042         var dir;
20043         
20044         switch(e.keyCode){
20045             case 27: // escape
20046                 this.hide();
20047                 e.preventDefault();
20048                 break;
20049             case 37: // left
20050             case 39: // right
20051                 dir = e.keyCode == 37 ? -1 : 1;
20052                 
20053                 this.vIndex = this.vIndex + dir;
20054                 
20055                 if(this.vIndex < 0){
20056                     this.vIndex = 0;
20057                 }
20058                 
20059                 if(this.vIndex > 11){
20060                     this.vIndex = 11;
20061                 }
20062                 
20063                 if(isNaN(this.vIndex)){
20064                     this.vIndex = 0;
20065                 }
20066                 
20067                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20068                 
20069                 break;
20070             case 38: // up
20071             case 40: // down
20072                 
20073                 dir = e.keyCode == 38 ? -1 : 1;
20074                 
20075                 this.vIndex = this.vIndex + dir * 4;
20076                 
20077                 if(this.vIndex < 0){
20078                     this.vIndex = 0;
20079                 }
20080                 
20081                 if(this.vIndex > 11){
20082                     this.vIndex = 11;
20083                 }
20084                 
20085                 if(isNaN(this.vIndex)){
20086                     this.vIndex = 0;
20087                 }
20088                 
20089                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20090                 break;
20091                 
20092             case 13: // enter
20093                 
20094                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20095                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20096                 }
20097                 
20098                 this.hide();
20099                 e.preventDefault();
20100                 break;
20101             case 9: // tab
20102                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20103                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20104                 }
20105                 this.hide();
20106                 break;
20107             case 16: // shift
20108             case 17: // ctrl
20109             case 18: // alt
20110                 break;
20111             default :
20112                 this.hide();
20113                 
20114         }
20115     },
20116     
20117     remove: function() 
20118     {
20119         this.picker().remove();
20120     }
20121    
20122 });
20123
20124 Roo.apply(Roo.bootstrap.MonthField,  {
20125     
20126     content : {
20127         tag: 'tbody',
20128         cn: [
20129         {
20130             tag: 'tr',
20131             cn: [
20132             {
20133                 tag: 'td',
20134                 colspan: '7'
20135             }
20136             ]
20137         }
20138         ]
20139     },
20140     
20141     dates:{
20142         en: {
20143             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20144             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20145         }
20146     }
20147 });
20148
20149 Roo.apply(Roo.bootstrap.MonthField,  {
20150   
20151     template : {
20152         tag: 'div',
20153         cls: 'datepicker dropdown-menu roo-dynamic',
20154         cn: [
20155             {
20156                 tag: 'div',
20157                 cls: 'datepicker-months',
20158                 cn: [
20159                 {
20160                     tag: 'table',
20161                     cls: 'table-condensed',
20162                     cn:[
20163                         Roo.bootstrap.DateField.content
20164                     ]
20165                 }
20166                 ]
20167             }
20168         ]
20169     }
20170 });
20171
20172  
20173
20174  
20175  /*
20176  * - LGPL
20177  *
20178  * CheckBox
20179  * 
20180  */
20181
20182 /**
20183  * @class Roo.bootstrap.CheckBox
20184  * @extends Roo.bootstrap.Input
20185  * Bootstrap CheckBox class
20186  * 
20187  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20188  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20189  * @cfg {String} boxLabel The text that appears beside the checkbox
20190  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20191  * @cfg {Boolean} checked initnal the element
20192  * @cfg {Boolean} inline inline the element (default false)
20193  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20194  * @cfg {String} tooltip label tooltip
20195  * 
20196  * @constructor
20197  * Create a new CheckBox
20198  * @param {Object} config The config object
20199  */
20200
20201 Roo.bootstrap.CheckBox = function(config){
20202     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20203    
20204     this.addEvents({
20205         /**
20206         * @event check
20207         * Fires when the element is checked or unchecked.
20208         * @param {Roo.bootstrap.CheckBox} this This input
20209         * @param {Boolean} checked The new checked value
20210         */
20211        check : true,
20212        /**
20213         * @event click
20214         * Fires when the element is click.
20215         * @param {Roo.bootstrap.CheckBox} this This input
20216         */
20217        click : true
20218     });
20219     
20220 };
20221
20222 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20223   
20224     inputType: 'checkbox',
20225     inputValue: 1,
20226     valueOff: 0,
20227     boxLabel: false,
20228     checked: false,
20229     weight : false,
20230     inline: false,
20231     tooltip : '',
20232     
20233     getAutoCreate : function()
20234     {
20235         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20236         
20237         var id = Roo.id();
20238         
20239         var cfg = {};
20240         
20241         cfg.cls = 'form-group ' + this.inputType; //input-group
20242         
20243         if(this.inline){
20244             cfg.cls += ' ' + this.inputType + '-inline';
20245         }
20246         
20247         var input =  {
20248             tag: 'input',
20249             id : id,
20250             type : this.inputType,
20251             value : this.inputValue,
20252             cls : 'roo-' + this.inputType, //'form-box',
20253             placeholder : this.placeholder || ''
20254             
20255         };
20256         
20257         if(this.inputType != 'radio'){
20258             var hidden =  {
20259                 tag: 'input',
20260                 type : 'hidden',
20261                 cls : 'roo-hidden-value',
20262                 value : this.checked ? this.inputValue : this.valueOff
20263             };
20264         }
20265         
20266             
20267         if (this.weight) { // Validity check?
20268             cfg.cls += " " + this.inputType + "-" + this.weight;
20269         }
20270         
20271         if (this.disabled) {
20272             input.disabled=true;
20273         }
20274         
20275         if(this.checked){
20276             input.checked = this.checked;
20277         }
20278         
20279         if (this.name) {
20280             
20281             input.name = this.name;
20282             
20283             if(this.inputType != 'radio'){
20284                 hidden.name = this.name;
20285                 input.name = '_hidden_' + this.name;
20286             }
20287         }
20288         
20289         if (this.size) {
20290             input.cls += ' input-' + this.size;
20291         }
20292         
20293         var settings=this;
20294         
20295         ['xs','sm','md','lg'].map(function(size){
20296             if (settings[size]) {
20297                 cfg.cls += ' col-' + size + '-' + settings[size];
20298             }
20299         });
20300         
20301         var inputblock = input;
20302          
20303         if (this.before || this.after) {
20304             
20305             inputblock = {
20306                 cls : 'input-group',
20307                 cn :  [] 
20308             };
20309             
20310             if (this.before) {
20311                 inputblock.cn.push({
20312                     tag :'span',
20313                     cls : 'input-group-addon',
20314                     html : this.before
20315                 });
20316             }
20317             
20318             inputblock.cn.push(input);
20319             
20320             if(this.inputType != 'radio'){
20321                 inputblock.cn.push(hidden);
20322             }
20323             
20324             if (this.after) {
20325                 inputblock.cn.push({
20326                     tag :'span',
20327                     cls : 'input-group-addon',
20328                     html : this.after
20329                 });
20330             }
20331             
20332         }
20333         
20334         if (align ==='left' && this.fieldLabel.length) {
20335 //                Roo.log("left and has label");
20336             cfg.cn = [
20337                 {
20338                     tag: 'label',
20339                     'for' :  id,
20340                     cls : 'control-label',
20341                     html : this.fieldLabel
20342                 },
20343                 {
20344                     cls : "", 
20345                     cn: [
20346                         inputblock
20347                     ]
20348                 }
20349             ];
20350             
20351             if(this.labelWidth > 12){
20352                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20353             }
20354             
20355             if(this.labelWidth < 13 && this.labelmd == 0){
20356                 this.labelmd = this.labelWidth;
20357             }
20358             
20359             if(this.labellg > 0){
20360                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20361                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20362             }
20363             
20364             if(this.labelmd > 0){
20365                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20366                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20367             }
20368             
20369             if(this.labelsm > 0){
20370                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20371                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20372             }
20373             
20374             if(this.labelxs > 0){
20375                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20376                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20377             }
20378             
20379         } else if ( this.fieldLabel.length) {
20380 //                Roo.log(" label");
20381                 cfg.cn = [
20382                    
20383                     {
20384                         tag: this.boxLabel ? 'span' : 'label',
20385                         'for': id,
20386                         cls: 'control-label box-input-label',
20387                         //cls : 'input-group-addon',
20388                         html : this.fieldLabel
20389                     },
20390                     
20391                     inputblock
20392                     
20393                 ];
20394
20395         } else {
20396             
20397 //                Roo.log(" no label && no align");
20398                 cfg.cn = [  inputblock ] ;
20399                 
20400                 
20401         }
20402         
20403         if(this.boxLabel){
20404              var boxLabelCfg = {
20405                 tag: 'label',
20406                 //'for': id, // box label is handled by onclick - so no for...
20407                 cls: 'box-label',
20408                 html: this.boxLabel
20409             };
20410             
20411             if(this.tooltip){
20412                 boxLabelCfg.tooltip = this.tooltip;
20413             }
20414              
20415             cfg.cn.push(boxLabelCfg);
20416         }
20417         
20418         if(this.inputType != 'radio'){
20419             cfg.cn.push(hidden);
20420         }
20421         
20422         return cfg;
20423         
20424     },
20425     
20426     /**
20427      * return the real input element.
20428      */
20429     inputEl: function ()
20430     {
20431         return this.el.select('input.roo-' + this.inputType,true).first();
20432     },
20433     hiddenEl: function ()
20434     {
20435         return this.el.select('input.roo-hidden-value',true).first();
20436     },
20437     
20438     labelEl: function()
20439     {
20440         return this.el.select('label.control-label',true).first();
20441     },
20442     /* depricated... */
20443     
20444     label: function()
20445     {
20446         return this.labelEl();
20447     },
20448     
20449     boxLabelEl: function()
20450     {
20451         return this.el.select('label.box-label',true).first();
20452     },
20453     
20454     initEvents : function()
20455     {
20456 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20457         
20458         this.inputEl().on('click', this.onClick,  this);
20459         
20460         if (this.boxLabel) { 
20461             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20462         }
20463         
20464         this.startValue = this.getValue();
20465         
20466         if(this.groupId){
20467             Roo.bootstrap.CheckBox.register(this);
20468         }
20469     },
20470     
20471     onClick : function(e)
20472     {   
20473         if(this.fireEvent('click', this, e) !== false){
20474             this.setChecked(!this.checked);
20475         }
20476         
20477     },
20478     
20479     setChecked : function(state,suppressEvent)
20480     {
20481         this.startValue = this.getValue();
20482
20483         if(this.inputType == 'radio'){
20484             
20485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20486                 e.dom.checked = false;
20487             });
20488             
20489             this.inputEl().dom.checked = true;
20490             
20491             this.inputEl().dom.value = this.inputValue;
20492             
20493             if(suppressEvent !== true){
20494                 this.fireEvent('check', this, true);
20495             }
20496             
20497             this.validate();
20498             
20499             return;
20500         }
20501         
20502         this.checked = state;
20503         
20504         this.inputEl().dom.checked = state;
20505         
20506         
20507         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20508         
20509         if(suppressEvent !== true){
20510             this.fireEvent('check', this, state);
20511         }
20512         
20513         this.validate();
20514     },
20515     
20516     getValue : function()
20517     {
20518         if(this.inputType == 'radio'){
20519             return this.getGroupValue();
20520         }
20521         
20522         return this.hiddenEl().dom.value;
20523         
20524     },
20525     
20526     getGroupValue : function()
20527     {
20528         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20529             return '';
20530         }
20531         
20532         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20533     },
20534     
20535     setValue : function(v,suppressEvent)
20536     {
20537         if(this.inputType == 'radio'){
20538             this.setGroupValue(v, suppressEvent);
20539             return;
20540         }
20541         
20542         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20543         
20544         this.validate();
20545     },
20546     
20547     setGroupValue : function(v, suppressEvent)
20548     {
20549         this.startValue = this.getValue();
20550         
20551         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20552             e.dom.checked = false;
20553             
20554             if(e.dom.value == v){
20555                 e.dom.checked = true;
20556             }
20557         });
20558         
20559         if(suppressEvent !== true){
20560             this.fireEvent('check', this, true);
20561         }
20562
20563         this.validate();
20564         
20565         return;
20566     },
20567     
20568     validate : function()
20569     {
20570         if(
20571                 this.disabled || 
20572                 (this.inputType == 'radio' && this.validateRadio()) ||
20573                 (this.inputType == 'checkbox' && this.validateCheckbox())
20574         ){
20575             this.markValid();
20576             return true;
20577         }
20578         
20579         this.markInvalid();
20580         return false;
20581     },
20582     
20583     validateRadio : function()
20584     {
20585         if(this.allowBlank){
20586             return true;
20587         }
20588         
20589         var valid = false;
20590         
20591         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20592             if(!e.dom.checked){
20593                 return;
20594             }
20595             
20596             valid = true;
20597             
20598             return false;
20599         });
20600         
20601         return valid;
20602     },
20603     
20604     validateCheckbox : function()
20605     {
20606         if(!this.groupId){
20607             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20608             //return (this.getValue() == this.inputValue) ? true : false;
20609         }
20610         
20611         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20612         
20613         if(!group){
20614             return false;
20615         }
20616         
20617         var r = false;
20618         
20619         for(var i in group){
20620             if(group[i].el.isVisible(true)){
20621                 r = false;
20622                 break;
20623             }
20624             
20625             r = true;
20626         }
20627         
20628         for(var i in group){
20629             if(r){
20630                 break;
20631             }
20632             
20633             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20634         }
20635         
20636         return r;
20637     },
20638     
20639     /**
20640      * Mark this field as valid
20641      */
20642     markValid : function()
20643     {
20644         var _this = this;
20645         
20646         this.fireEvent('valid', this);
20647         
20648         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20649         
20650         if(this.groupId){
20651             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20652         }
20653         
20654         if(label){
20655             label.markValid();
20656         }
20657
20658         if(this.inputType == 'radio'){
20659             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20661                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20662             });
20663             
20664             return;
20665         }
20666
20667         if(!this.groupId){
20668             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20669             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20670             return;
20671         }
20672         
20673         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20674         
20675         if(!group){
20676             return;
20677         }
20678         
20679         for(var i in group){
20680             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20681             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20682         }
20683     },
20684     
20685      /**
20686      * Mark this field as invalid
20687      * @param {String} msg The validation message
20688      */
20689     markInvalid : function(msg)
20690     {
20691         if(this.allowBlank){
20692             return;
20693         }
20694         
20695         var _this = this;
20696         
20697         this.fireEvent('invalid', this, msg);
20698         
20699         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20700         
20701         if(this.groupId){
20702             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20703         }
20704         
20705         if(label){
20706             label.markInvalid();
20707         }
20708             
20709         if(this.inputType == 'radio'){
20710             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20711                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20712                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20713             });
20714             
20715             return;
20716         }
20717         
20718         if(!this.groupId){
20719             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20720             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20721             return;
20722         }
20723         
20724         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20725         
20726         if(!group){
20727             return;
20728         }
20729         
20730         for(var i in group){
20731             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20732             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20733         }
20734         
20735     },
20736     
20737     clearInvalid : function()
20738     {
20739         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20740         
20741         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20742         
20743         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20744         
20745         if (label && label.iconEl) {
20746             label.iconEl.removeClass(label.validClass);
20747             label.iconEl.removeClass(label.invalidClass);
20748         }
20749     },
20750     
20751     disable : function()
20752     {
20753         if(this.inputType != 'radio'){
20754             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20755             return;
20756         }
20757         
20758         var _this = this;
20759         
20760         if(this.rendered){
20761             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20762                 _this.getActionEl().addClass(this.disabledClass);
20763                 e.dom.disabled = true;
20764             });
20765         }
20766         
20767         this.disabled = true;
20768         this.fireEvent("disable", this);
20769         return this;
20770     },
20771
20772     enable : function()
20773     {
20774         if(this.inputType != 'radio'){
20775             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20776             return;
20777         }
20778         
20779         var _this = this;
20780         
20781         if(this.rendered){
20782             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20783                 _this.getActionEl().removeClass(this.disabledClass);
20784                 e.dom.disabled = false;
20785             });
20786         }
20787         
20788         this.disabled = false;
20789         this.fireEvent("enable", this);
20790         return this;
20791     },
20792     
20793     setBoxLabel : function(v)
20794     {
20795         this.boxLabel = v;
20796         
20797         if(this.rendered){
20798             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20799         }
20800     }
20801
20802 });
20803
20804 Roo.apply(Roo.bootstrap.CheckBox, {
20805     
20806     groups: {},
20807     
20808      /**
20809     * register a CheckBox Group
20810     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20811     */
20812     register : function(checkbox)
20813     {
20814         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20815             this.groups[checkbox.groupId] = {};
20816         }
20817         
20818         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20819             return;
20820         }
20821         
20822         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20823         
20824     },
20825     /**
20826     * fetch a CheckBox Group based on the group ID
20827     * @param {string} the group ID
20828     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20829     */
20830     get: function(groupId) {
20831         if (typeof(this.groups[groupId]) == 'undefined') {
20832             return false;
20833         }
20834         
20835         return this.groups[groupId] ;
20836     }
20837     
20838     
20839 });
20840 /*
20841  * - LGPL
20842  *
20843  * RadioItem
20844  * 
20845  */
20846
20847 /**
20848  * @class Roo.bootstrap.Radio
20849  * @extends Roo.bootstrap.Component
20850  * Bootstrap Radio class
20851  * @cfg {String} boxLabel - the label associated
20852  * @cfg {String} value - the value of radio
20853  * 
20854  * @constructor
20855  * Create a new Radio
20856  * @param {Object} config The config object
20857  */
20858 Roo.bootstrap.Radio = function(config){
20859     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20860     
20861 };
20862
20863 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20864     
20865     boxLabel : '',
20866     
20867     value : '',
20868     
20869     getAutoCreate : function()
20870     {
20871         var cfg = {
20872             tag : 'div',
20873             cls : 'form-group radio',
20874             cn : [
20875                 {
20876                     tag : 'label',
20877                     cls : 'box-label',
20878                     html : this.boxLabel
20879                 }
20880             ]
20881         };
20882         
20883         return cfg;
20884     },
20885     
20886     initEvents : function() 
20887     {
20888         this.parent().register(this);
20889         
20890         this.el.on('click', this.onClick, this);
20891         
20892     },
20893     
20894     onClick : function()
20895     {
20896         this.setChecked(true);
20897     },
20898     
20899     setChecked : function(state, suppressEvent)
20900     {
20901         this.parent().setValue(this.value, suppressEvent);
20902         
20903     },
20904     
20905     setBoxLabel : function(v)
20906     {
20907         this.boxLabel = v;
20908         
20909         if(this.rendered){
20910             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20911         }
20912     }
20913     
20914 });
20915  
20916
20917  /*
20918  * - LGPL
20919  *
20920  * Input
20921  * 
20922  */
20923
20924 /**
20925  * @class Roo.bootstrap.SecurePass
20926  * @extends Roo.bootstrap.Input
20927  * Bootstrap SecurePass class
20928  *
20929  * 
20930  * @constructor
20931  * Create a new SecurePass
20932  * @param {Object} config The config object
20933  */
20934  
20935 Roo.bootstrap.SecurePass = function (config) {
20936     // these go here, so the translation tool can replace them..
20937     this.errors = {
20938         PwdEmpty: "Please type a password, and then retype it to confirm.",
20939         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20940         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20941         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20942         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20943         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20944         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20945         TooWeak: "Your password is Too Weak."
20946     },
20947     this.meterLabel = "Password strength:";
20948     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20949     this.meterClass = [
20950         "roo-password-meter-tooweak", 
20951         "roo-password-meter-weak", 
20952         "roo-password-meter-medium", 
20953         "roo-password-meter-strong", 
20954         "roo-password-meter-grey"
20955     ];
20956     
20957     this.errors = {};
20958     
20959     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20960 }
20961
20962 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20963     /**
20964      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20965      * {
20966      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20967      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20968      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20969      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20970      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20971      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20972      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20973      * })
20974      */
20975     // private
20976     
20977     meterWidth: 300,
20978     errorMsg :'',    
20979     errors: false,
20980     imageRoot: '/',
20981     /**
20982      * @cfg {String/Object} Label for the strength meter (defaults to
20983      * 'Password strength:')
20984      */
20985     // private
20986     meterLabel: '',
20987     /**
20988      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20989      * ['Weak', 'Medium', 'Strong'])
20990      */
20991     // private    
20992     pwdStrengths: false,    
20993     // private
20994     strength: 0,
20995     // private
20996     _lastPwd: null,
20997     // private
20998     kCapitalLetter: 0,
20999     kSmallLetter: 1,
21000     kDigit: 2,
21001     kPunctuation: 3,
21002     
21003     insecure: false,
21004     // private
21005     initEvents: function ()
21006     {
21007         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21008
21009         if (this.el.is('input[type=password]') && Roo.isSafari) {
21010             this.el.on('keydown', this.SafariOnKeyDown, this);
21011         }
21012
21013         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21014     },
21015     // private
21016     onRender: function (ct, position)
21017     {
21018         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21019         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21020         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21021
21022         this.trigger.createChild({
21023                    cn: [
21024                     {
21025                     //id: 'PwdMeter',
21026                     tag: 'div',
21027                     cls: 'roo-password-meter-grey col-xs-12',
21028                     style: {
21029                         //width: 0,
21030                         //width: this.meterWidth + 'px'                                                
21031                         }
21032                     },
21033                     {                            
21034                          cls: 'roo-password-meter-text'                          
21035                     }
21036                 ]            
21037         });
21038
21039          
21040         if (this.hideTrigger) {
21041             this.trigger.setDisplayed(false);
21042         }
21043         this.setSize(this.width || '', this.height || '');
21044     },
21045     // private
21046     onDestroy: function ()
21047     {
21048         if (this.trigger) {
21049             this.trigger.removeAllListeners();
21050             this.trigger.remove();
21051         }
21052         if (this.wrap) {
21053             this.wrap.remove();
21054         }
21055         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21056     },
21057     // private
21058     checkStrength: function ()
21059     {
21060         var pwd = this.inputEl().getValue();
21061         if (pwd == this._lastPwd) {
21062             return;
21063         }
21064
21065         var strength;
21066         if (this.ClientSideStrongPassword(pwd)) {
21067             strength = 3;
21068         } else if (this.ClientSideMediumPassword(pwd)) {
21069             strength = 2;
21070         } else if (this.ClientSideWeakPassword(pwd)) {
21071             strength = 1;
21072         } else {
21073             strength = 0;
21074         }
21075         
21076         Roo.log('strength1: ' + strength);
21077         
21078         //var pm = this.trigger.child('div/div/div').dom;
21079         var pm = this.trigger.child('div/div');
21080         pm.removeClass(this.meterClass);
21081         pm.addClass(this.meterClass[strength]);
21082                 
21083         
21084         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21085                 
21086         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21087         
21088         this._lastPwd = pwd;
21089     },
21090     reset: function ()
21091     {
21092         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21093         
21094         this._lastPwd = '';
21095         
21096         var pm = this.trigger.child('div/div');
21097         pm.removeClass(this.meterClass);
21098         pm.addClass('roo-password-meter-grey');        
21099         
21100         
21101         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21102         
21103         pt.innerHTML = '';
21104         this.inputEl().dom.type='password';
21105     },
21106     // private
21107     validateValue: function (value)
21108     {
21109         
21110         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21111             return false;
21112         }
21113         if (value.length == 0) {
21114             if (this.allowBlank) {
21115                 this.clearInvalid();
21116                 return true;
21117             }
21118
21119             this.markInvalid(this.errors.PwdEmpty);
21120             this.errorMsg = this.errors.PwdEmpty;
21121             return false;
21122         }
21123         
21124         if(this.insecure){
21125             return true;
21126         }
21127         
21128         if ('[\x21-\x7e]*'.match(value)) {
21129             this.markInvalid(this.errors.PwdBadChar);
21130             this.errorMsg = this.errors.PwdBadChar;
21131             return false;
21132         }
21133         if (value.length < 6) {
21134             this.markInvalid(this.errors.PwdShort);
21135             this.errorMsg = this.errors.PwdShort;
21136             return false;
21137         }
21138         if (value.length > 16) {
21139             this.markInvalid(this.errors.PwdLong);
21140             this.errorMsg = this.errors.PwdLong;
21141             return false;
21142         }
21143         var strength;
21144         if (this.ClientSideStrongPassword(value)) {
21145             strength = 3;
21146         } else if (this.ClientSideMediumPassword(value)) {
21147             strength = 2;
21148         } else if (this.ClientSideWeakPassword(value)) {
21149             strength = 1;
21150         } else {
21151             strength = 0;
21152         }
21153
21154         
21155         if (strength < 2) {
21156             //this.markInvalid(this.errors.TooWeak);
21157             this.errorMsg = this.errors.TooWeak;
21158             //return false;
21159         }
21160         
21161         
21162         console.log('strength2: ' + strength);
21163         
21164         //var pm = this.trigger.child('div/div/div').dom;
21165         
21166         var pm = this.trigger.child('div/div');
21167         pm.removeClass(this.meterClass);
21168         pm.addClass(this.meterClass[strength]);
21169                 
21170         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21171                 
21172         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21173         
21174         this.errorMsg = ''; 
21175         return true;
21176     },
21177     // private
21178     CharacterSetChecks: function (type)
21179     {
21180         this.type = type;
21181         this.fResult = false;
21182     },
21183     // private
21184     isctype: function (character, type)
21185     {
21186         switch (type) {  
21187             case this.kCapitalLetter:
21188                 if (character >= 'A' && character <= 'Z') {
21189                     return true;
21190                 }
21191                 break;
21192             
21193             case this.kSmallLetter:
21194                 if (character >= 'a' && character <= 'z') {
21195                     return true;
21196                 }
21197                 break;
21198             
21199             case this.kDigit:
21200                 if (character >= '0' && character <= '9') {
21201                     return true;
21202                 }
21203                 break;
21204             
21205             case this.kPunctuation:
21206                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21207                     return true;
21208                 }
21209                 break;
21210             
21211             default:
21212                 return false;
21213         }
21214
21215     },
21216     // private
21217     IsLongEnough: function (pwd, size)
21218     {
21219         return !(pwd == null || isNaN(size) || pwd.length < size);
21220     },
21221     // private
21222     SpansEnoughCharacterSets: function (word, nb)
21223     {
21224         if (!this.IsLongEnough(word, nb))
21225         {
21226             return false;
21227         }
21228
21229         var characterSetChecks = new Array(
21230             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21231             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21232         );
21233         
21234         for (var index = 0; index < word.length; ++index) {
21235             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21236                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21237                     characterSetChecks[nCharSet].fResult = true;
21238                     break;
21239                 }
21240             }
21241         }
21242
21243         var nCharSets = 0;
21244         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21245             if (characterSetChecks[nCharSet].fResult) {
21246                 ++nCharSets;
21247             }
21248         }
21249
21250         if (nCharSets < nb) {
21251             return false;
21252         }
21253         return true;
21254     },
21255     // private
21256     ClientSideStrongPassword: function (pwd)
21257     {
21258         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21259     },
21260     // private
21261     ClientSideMediumPassword: function (pwd)
21262     {
21263         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21264     },
21265     // private
21266     ClientSideWeakPassword: function (pwd)
21267     {
21268         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21269     }
21270           
21271 })//<script type="text/javascript">
21272
21273 /*
21274  * Based  Ext JS Library 1.1.1
21275  * Copyright(c) 2006-2007, Ext JS, LLC.
21276  * LGPL
21277  *
21278  */
21279  
21280 /**
21281  * @class Roo.HtmlEditorCore
21282  * @extends Roo.Component
21283  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21284  *
21285  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21286  */
21287
21288 Roo.HtmlEditorCore = function(config){
21289     
21290     
21291     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21292     
21293     
21294     this.addEvents({
21295         /**
21296          * @event initialize
21297          * Fires when the editor is fully initialized (including the iframe)
21298          * @param {Roo.HtmlEditorCore} this
21299          */
21300         initialize: true,
21301         /**
21302          * @event activate
21303          * Fires when the editor is first receives the focus. Any insertion must wait
21304          * until after this event.
21305          * @param {Roo.HtmlEditorCore} this
21306          */
21307         activate: true,
21308          /**
21309          * @event beforesync
21310          * Fires before the textarea is updated with content from the editor iframe. Return false
21311          * to cancel the sync.
21312          * @param {Roo.HtmlEditorCore} this
21313          * @param {String} html
21314          */
21315         beforesync: true,
21316          /**
21317          * @event beforepush
21318          * Fires before the iframe editor is updated with content from the textarea. Return false
21319          * to cancel the push.
21320          * @param {Roo.HtmlEditorCore} this
21321          * @param {String} html
21322          */
21323         beforepush: true,
21324          /**
21325          * @event sync
21326          * Fires when the textarea is updated with content from the editor iframe.
21327          * @param {Roo.HtmlEditorCore} this
21328          * @param {String} html
21329          */
21330         sync: true,
21331          /**
21332          * @event push
21333          * Fires when the iframe editor is updated with content from the textarea.
21334          * @param {Roo.HtmlEditorCore} this
21335          * @param {String} html
21336          */
21337         push: true,
21338         
21339         /**
21340          * @event editorevent
21341          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21342          * @param {Roo.HtmlEditorCore} this
21343          */
21344         editorevent: true
21345         
21346     });
21347     
21348     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21349     
21350     // defaults : white / black...
21351     this.applyBlacklists();
21352     
21353     
21354     
21355 };
21356
21357
21358 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21359
21360
21361      /**
21362      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21363      */
21364     
21365     owner : false,
21366     
21367      /**
21368      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21369      *                        Roo.resizable.
21370      */
21371     resizable : false,
21372      /**
21373      * @cfg {Number} height (in pixels)
21374      */   
21375     height: 300,
21376    /**
21377      * @cfg {Number} width (in pixels)
21378      */   
21379     width: 500,
21380     
21381     /**
21382      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21383      * 
21384      */
21385     stylesheets: false,
21386     
21387     // id of frame..
21388     frameId: false,
21389     
21390     // private properties
21391     validationEvent : false,
21392     deferHeight: true,
21393     initialized : false,
21394     activated : false,
21395     sourceEditMode : false,
21396     onFocus : Roo.emptyFn,
21397     iframePad:3,
21398     hideMode:'offsets',
21399     
21400     clearUp: true,
21401     
21402     // blacklist + whitelisted elements..
21403     black: false,
21404     white: false,
21405      
21406     bodyCls : '',
21407
21408     /**
21409      * Protected method that will not generally be called directly. It
21410      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21411      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21412      */
21413     getDocMarkup : function(){
21414         // body styles..
21415         var st = '';
21416         
21417         // inherit styels from page...?? 
21418         if (this.stylesheets === false) {
21419             
21420             Roo.get(document.head).select('style').each(function(node) {
21421                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21422             });
21423             
21424             Roo.get(document.head).select('link').each(function(node) { 
21425                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21426             });
21427             
21428         } else if (!this.stylesheets.length) {
21429                 // simple..
21430                 st = '<style type="text/css">' +
21431                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21432                    '</style>';
21433         } else { 
21434             st = '<style type="text/css">' +
21435                     this.stylesheets +
21436                 '</style>';
21437         }
21438         
21439         st +=  '<style type="text/css">' +
21440             'IMG { cursor: pointer } ' +
21441         '</style>';
21442
21443         var cls = 'roo-htmleditor-body';
21444         
21445         if(this.bodyCls.length){
21446             cls += ' ' + this.bodyCls;
21447         }
21448         
21449         return '<html><head>' + st  +
21450             //<style type="text/css">' +
21451             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21452             //'</style>' +
21453             ' </head><body class="' +  cls + '"></body></html>';
21454     },
21455
21456     // private
21457     onRender : function(ct, position)
21458     {
21459         var _t = this;
21460         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21461         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21462         
21463         
21464         this.el.dom.style.border = '0 none';
21465         this.el.dom.setAttribute('tabIndex', -1);
21466         this.el.addClass('x-hidden hide');
21467         
21468         
21469         
21470         if(Roo.isIE){ // fix IE 1px bogus margin
21471             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21472         }
21473        
21474         
21475         this.frameId = Roo.id();
21476         
21477          
21478         
21479         var iframe = this.owner.wrap.createChild({
21480             tag: 'iframe',
21481             cls: 'form-control', // bootstrap..
21482             id: this.frameId,
21483             name: this.frameId,
21484             frameBorder : 'no',
21485             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21486         }, this.el
21487         );
21488         
21489         
21490         this.iframe = iframe.dom;
21491
21492          this.assignDocWin();
21493         
21494         this.doc.designMode = 'on';
21495        
21496         this.doc.open();
21497         this.doc.write(this.getDocMarkup());
21498         this.doc.close();
21499
21500         
21501         var task = { // must defer to wait for browser to be ready
21502             run : function(){
21503                 //console.log("run task?" + this.doc.readyState);
21504                 this.assignDocWin();
21505                 if(this.doc.body || this.doc.readyState == 'complete'){
21506                     try {
21507                         this.doc.designMode="on";
21508                     } catch (e) {
21509                         return;
21510                     }
21511                     Roo.TaskMgr.stop(task);
21512                     this.initEditor.defer(10, this);
21513                 }
21514             },
21515             interval : 10,
21516             duration: 10000,
21517             scope: this
21518         };
21519         Roo.TaskMgr.start(task);
21520
21521     },
21522
21523     // private
21524     onResize : function(w, h)
21525     {
21526          Roo.log('resize: ' +w + ',' + h );
21527         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21528         if(!this.iframe){
21529             return;
21530         }
21531         if(typeof w == 'number'){
21532             
21533             this.iframe.style.width = w + 'px';
21534         }
21535         if(typeof h == 'number'){
21536             
21537             this.iframe.style.height = h + 'px';
21538             if(this.doc){
21539                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21540             }
21541         }
21542         
21543     },
21544
21545     /**
21546      * Toggles the editor between standard and source edit mode.
21547      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21548      */
21549     toggleSourceEdit : function(sourceEditMode){
21550         
21551         this.sourceEditMode = sourceEditMode === true;
21552         
21553         if(this.sourceEditMode){
21554  
21555             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21556             
21557         }else{
21558             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21559             //this.iframe.className = '';
21560             this.deferFocus();
21561         }
21562         //this.setSize(this.owner.wrap.getSize());
21563         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21564     },
21565
21566     
21567   
21568
21569     /**
21570      * Protected method that will not generally be called directly. If you need/want
21571      * custom HTML cleanup, this is the method you should override.
21572      * @param {String} html The HTML to be cleaned
21573      * return {String} The cleaned HTML
21574      */
21575     cleanHtml : function(html){
21576         html = String(html);
21577         if(html.length > 5){
21578             if(Roo.isSafari){ // strip safari nonsense
21579                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21580             }
21581         }
21582         if(html == '&nbsp;'){
21583             html = '';
21584         }
21585         return html;
21586     },
21587
21588     /**
21589      * HTML Editor -> Textarea
21590      * Protected method that will not generally be called directly. Syncs the contents
21591      * of the editor iframe with the textarea.
21592      */
21593     syncValue : function(){
21594         if(this.initialized){
21595             var bd = (this.doc.body || this.doc.documentElement);
21596             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21597             var html = bd.innerHTML;
21598             if(Roo.isSafari){
21599                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21600                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21601                 if(m && m[1]){
21602                     html = '<div style="'+m[0]+'">' + html + '</div>';
21603                 }
21604             }
21605             html = this.cleanHtml(html);
21606             // fix up the special chars.. normaly like back quotes in word...
21607             // however we do not want to do this with chinese..
21608             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21609                 var cc = b.charCodeAt();
21610                 if (
21611                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21612                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21613                     (cc >= 0xf900 && cc < 0xfb00 )
21614                 ) {
21615                         return b;
21616                 }
21617                 return "&#"+cc+";" 
21618             });
21619             if(this.owner.fireEvent('beforesync', this, html) !== false){
21620                 this.el.dom.value = html;
21621                 this.owner.fireEvent('sync', this, html);
21622             }
21623         }
21624     },
21625
21626     /**
21627      * Protected method that will not generally be called directly. Pushes the value of the textarea
21628      * into the iframe editor.
21629      */
21630     pushValue : function(){
21631         if(this.initialized){
21632             var v = this.el.dom.value.trim();
21633             
21634 //            if(v.length < 1){
21635 //                v = '&#160;';
21636 //            }
21637             
21638             if(this.owner.fireEvent('beforepush', this, v) !== false){
21639                 var d = (this.doc.body || this.doc.documentElement);
21640                 d.innerHTML = v;
21641                 this.cleanUpPaste();
21642                 this.el.dom.value = d.innerHTML;
21643                 this.owner.fireEvent('push', this, v);
21644             }
21645         }
21646     },
21647
21648     // private
21649     deferFocus : function(){
21650         this.focus.defer(10, this);
21651     },
21652
21653     // doc'ed in Field
21654     focus : function(){
21655         if(this.win && !this.sourceEditMode){
21656             this.win.focus();
21657         }else{
21658             this.el.focus();
21659         }
21660     },
21661     
21662     assignDocWin: function()
21663     {
21664         var iframe = this.iframe;
21665         
21666          if(Roo.isIE){
21667             this.doc = iframe.contentWindow.document;
21668             this.win = iframe.contentWindow;
21669         } else {
21670 //            if (!Roo.get(this.frameId)) {
21671 //                return;
21672 //            }
21673 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21674 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21675             
21676             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21677                 return;
21678             }
21679             
21680             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21681             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21682         }
21683     },
21684     
21685     // private
21686     initEditor : function(){
21687         //console.log("INIT EDITOR");
21688         this.assignDocWin();
21689         
21690         
21691         
21692         this.doc.designMode="on";
21693         this.doc.open();
21694         this.doc.write(this.getDocMarkup());
21695         this.doc.close();
21696         
21697         var dbody = (this.doc.body || this.doc.documentElement);
21698         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21699         // this copies styles from the containing element into thsi one..
21700         // not sure why we need all of this..
21701         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21702         
21703         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21704         //ss['background-attachment'] = 'fixed'; // w3c
21705         dbody.bgProperties = 'fixed'; // ie
21706         //Roo.DomHelper.applyStyles(dbody, ss);
21707         Roo.EventManager.on(this.doc, {
21708             //'mousedown': this.onEditorEvent,
21709             'mouseup': this.onEditorEvent,
21710             'dblclick': this.onEditorEvent,
21711             'click': this.onEditorEvent,
21712             'keyup': this.onEditorEvent,
21713             buffer:100,
21714             scope: this
21715         });
21716         if(Roo.isGecko){
21717             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21718         }
21719         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21720             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21721         }
21722         this.initialized = true;
21723
21724         this.owner.fireEvent('initialize', this);
21725         this.pushValue();
21726     },
21727
21728     // private
21729     onDestroy : function(){
21730         
21731         
21732         
21733         if(this.rendered){
21734             
21735             //for (var i =0; i < this.toolbars.length;i++) {
21736             //    // fixme - ask toolbars for heights?
21737             //    this.toolbars[i].onDestroy();
21738            // }
21739             
21740             //this.wrap.dom.innerHTML = '';
21741             //this.wrap.remove();
21742         }
21743     },
21744
21745     // private
21746     onFirstFocus : function(){
21747         
21748         this.assignDocWin();
21749         
21750         
21751         this.activated = true;
21752          
21753     
21754         if(Roo.isGecko){ // prevent silly gecko errors
21755             this.win.focus();
21756             var s = this.win.getSelection();
21757             if(!s.focusNode || s.focusNode.nodeType != 3){
21758                 var r = s.getRangeAt(0);
21759                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21760                 r.collapse(true);
21761                 this.deferFocus();
21762             }
21763             try{
21764                 this.execCmd('useCSS', true);
21765                 this.execCmd('styleWithCSS', false);
21766             }catch(e){}
21767         }
21768         this.owner.fireEvent('activate', this);
21769     },
21770
21771     // private
21772     adjustFont: function(btn){
21773         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21774         //if(Roo.isSafari){ // safari
21775         //    adjust *= 2;
21776        // }
21777         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21778         if(Roo.isSafari){ // safari
21779             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21780             v =  (v < 10) ? 10 : v;
21781             v =  (v > 48) ? 48 : v;
21782             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21783             
21784         }
21785         
21786         
21787         v = Math.max(1, v+adjust);
21788         
21789         this.execCmd('FontSize', v  );
21790     },
21791
21792     onEditorEvent : function(e)
21793     {
21794         this.owner.fireEvent('editorevent', this, e);
21795       //  this.updateToolbar();
21796         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21797     },
21798
21799     insertTag : function(tg)
21800     {
21801         // could be a bit smarter... -> wrap the current selected tRoo..
21802         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21803             
21804             range = this.createRange(this.getSelection());
21805             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21806             wrappingNode.appendChild(range.extractContents());
21807             range.insertNode(wrappingNode);
21808
21809             return;
21810             
21811             
21812             
21813         }
21814         this.execCmd("formatblock",   tg);
21815         
21816     },
21817     
21818     insertText : function(txt)
21819     {
21820         
21821         
21822         var range = this.createRange();
21823         range.deleteContents();
21824                //alert(Sender.getAttribute('label'));
21825                
21826         range.insertNode(this.doc.createTextNode(txt));
21827     } ,
21828     
21829      
21830
21831     /**
21832      * Executes a Midas editor command on the editor document and performs necessary focus and
21833      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21834      * @param {String} cmd The Midas command
21835      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21836      */
21837     relayCmd : function(cmd, value){
21838         this.win.focus();
21839         this.execCmd(cmd, value);
21840         this.owner.fireEvent('editorevent', this);
21841         //this.updateToolbar();
21842         this.owner.deferFocus();
21843     },
21844
21845     /**
21846      * Executes a Midas editor command directly on the editor document.
21847      * For visual commands, you should use {@link #relayCmd} instead.
21848      * <b>This should only be called after the editor is initialized.</b>
21849      * @param {String} cmd The Midas command
21850      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21851      */
21852     execCmd : function(cmd, value){
21853         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21854         this.syncValue();
21855     },
21856  
21857  
21858    
21859     /**
21860      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21861      * to insert tRoo.
21862      * @param {String} text | dom node.. 
21863      */
21864     insertAtCursor : function(text)
21865     {
21866         
21867         if(!this.activated){
21868             return;
21869         }
21870         /*
21871         if(Roo.isIE){
21872             this.win.focus();
21873             var r = this.doc.selection.createRange();
21874             if(r){
21875                 r.collapse(true);
21876                 r.pasteHTML(text);
21877                 this.syncValue();
21878                 this.deferFocus();
21879             
21880             }
21881             return;
21882         }
21883         */
21884         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21885             this.win.focus();
21886             
21887             
21888             // from jquery ui (MIT licenced)
21889             var range, node;
21890             var win = this.win;
21891             
21892             if (win.getSelection && win.getSelection().getRangeAt) {
21893                 range = win.getSelection().getRangeAt(0);
21894                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21895                 range.insertNode(node);
21896             } else if (win.document.selection && win.document.selection.createRange) {
21897                 // no firefox support
21898                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21899                 win.document.selection.createRange().pasteHTML(txt);
21900             } else {
21901                 // no firefox support
21902                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21903                 this.execCmd('InsertHTML', txt);
21904             } 
21905             
21906             this.syncValue();
21907             
21908             this.deferFocus();
21909         }
21910     },
21911  // private
21912     mozKeyPress : function(e){
21913         if(e.ctrlKey){
21914             var c = e.getCharCode(), cmd;
21915           
21916             if(c > 0){
21917                 c = String.fromCharCode(c).toLowerCase();
21918                 switch(c){
21919                     case 'b':
21920                         cmd = 'bold';
21921                         break;
21922                     case 'i':
21923                         cmd = 'italic';
21924                         break;
21925                     
21926                     case 'u':
21927                         cmd = 'underline';
21928                         break;
21929                     
21930                     case 'v':
21931                         this.cleanUpPaste.defer(100, this);
21932                         return;
21933                         
21934                 }
21935                 if(cmd){
21936                     this.win.focus();
21937                     this.execCmd(cmd);
21938                     this.deferFocus();
21939                     e.preventDefault();
21940                 }
21941                 
21942             }
21943         }
21944     },
21945
21946     // private
21947     fixKeys : function(){ // load time branching for fastest keydown performance
21948         if(Roo.isIE){
21949             return function(e){
21950                 var k = e.getKey(), r;
21951                 if(k == e.TAB){
21952                     e.stopEvent();
21953                     r = this.doc.selection.createRange();
21954                     if(r){
21955                         r.collapse(true);
21956                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21957                         this.deferFocus();
21958                     }
21959                     return;
21960                 }
21961                 
21962                 if(k == e.ENTER){
21963                     r = this.doc.selection.createRange();
21964                     if(r){
21965                         var target = r.parentElement();
21966                         if(!target || target.tagName.toLowerCase() != 'li'){
21967                             e.stopEvent();
21968                             r.pasteHTML('<br />');
21969                             r.collapse(false);
21970                             r.select();
21971                         }
21972                     }
21973                 }
21974                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21975                     this.cleanUpPaste.defer(100, this);
21976                     return;
21977                 }
21978                 
21979                 
21980             };
21981         }else if(Roo.isOpera){
21982             return function(e){
21983                 var k = e.getKey();
21984                 if(k == e.TAB){
21985                     e.stopEvent();
21986                     this.win.focus();
21987                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21988                     this.deferFocus();
21989                 }
21990                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21991                     this.cleanUpPaste.defer(100, this);
21992                     return;
21993                 }
21994                 
21995             };
21996         }else if(Roo.isSafari){
21997             return function(e){
21998                 var k = e.getKey();
21999                 
22000                 if(k == e.TAB){
22001                     e.stopEvent();
22002                     this.execCmd('InsertText','\t');
22003                     this.deferFocus();
22004                     return;
22005                 }
22006                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22007                     this.cleanUpPaste.defer(100, this);
22008                     return;
22009                 }
22010                 
22011              };
22012         }
22013     }(),
22014     
22015     getAllAncestors: function()
22016     {
22017         var p = this.getSelectedNode();
22018         var a = [];
22019         if (!p) {
22020             a.push(p); // push blank onto stack..
22021             p = this.getParentElement();
22022         }
22023         
22024         
22025         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22026             a.push(p);
22027             p = p.parentNode;
22028         }
22029         a.push(this.doc.body);
22030         return a;
22031     },
22032     lastSel : false,
22033     lastSelNode : false,
22034     
22035     
22036     getSelection : function() 
22037     {
22038         this.assignDocWin();
22039         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22040     },
22041     
22042     getSelectedNode: function() 
22043     {
22044         // this may only work on Gecko!!!
22045         
22046         // should we cache this!!!!
22047         
22048         
22049         
22050          
22051         var range = this.createRange(this.getSelection()).cloneRange();
22052         
22053         if (Roo.isIE) {
22054             var parent = range.parentElement();
22055             while (true) {
22056                 var testRange = range.duplicate();
22057                 testRange.moveToElementText(parent);
22058                 if (testRange.inRange(range)) {
22059                     break;
22060                 }
22061                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22062                     break;
22063                 }
22064                 parent = parent.parentElement;
22065             }
22066             return parent;
22067         }
22068         
22069         // is ancestor a text element.
22070         var ac =  range.commonAncestorContainer;
22071         if (ac.nodeType == 3) {
22072             ac = ac.parentNode;
22073         }
22074         
22075         var ar = ac.childNodes;
22076          
22077         var nodes = [];
22078         var other_nodes = [];
22079         var has_other_nodes = false;
22080         for (var i=0;i<ar.length;i++) {
22081             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22082                 continue;
22083             }
22084             // fullly contained node.
22085             
22086             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22087                 nodes.push(ar[i]);
22088                 continue;
22089             }
22090             
22091             // probably selected..
22092             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22093                 other_nodes.push(ar[i]);
22094                 continue;
22095             }
22096             // outer..
22097             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22098                 continue;
22099             }
22100             
22101             
22102             has_other_nodes = true;
22103         }
22104         if (!nodes.length && other_nodes.length) {
22105             nodes= other_nodes;
22106         }
22107         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22108             return false;
22109         }
22110         
22111         return nodes[0];
22112     },
22113     createRange: function(sel)
22114     {
22115         // this has strange effects when using with 
22116         // top toolbar - not sure if it's a great idea.
22117         //this.editor.contentWindow.focus();
22118         if (typeof sel != "undefined") {
22119             try {
22120                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22121             } catch(e) {
22122                 return this.doc.createRange();
22123             }
22124         } else {
22125             return this.doc.createRange();
22126         }
22127     },
22128     getParentElement: function()
22129     {
22130         
22131         this.assignDocWin();
22132         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22133         
22134         var range = this.createRange(sel);
22135          
22136         try {
22137             var p = range.commonAncestorContainer;
22138             while (p.nodeType == 3) { // text node
22139                 p = p.parentNode;
22140             }
22141             return p;
22142         } catch (e) {
22143             return null;
22144         }
22145     
22146     },
22147     /***
22148      *
22149      * Range intersection.. the hard stuff...
22150      *  '-1' = before
22151      *  '0' = hits..
22152      *  '1' = after.
22153      *         [ -- selected range --- ]
22154      *   [fail]                        [fail]
22155      *
22156      *    basically..
22157      *      if end is before start or  hits it. fail.
22158      *      if start is after end or hits it fail.
22159      *
22160      *   if either hits (but other is outside. - then it's not 
22161      *   
22162      *    
22163      **/
22164     
22165     
22166     // @see http://www.thismuchiknow.co.uk/?p=64.
22167     rangeIntersectsNode : function(range, node)
22168     {
22169         var nodeRange = node.ownerDocument.createRange();
22170         try {
22171             nodeRange.selectNode(node);
22172         } catch (e) {
22173             nodeRange.selectNodeContents(node);
22174         }
22175     
22176         var rangeStartRange = range.cloneRange();
22177         rangeStartRange.collapse(true);
22178     
22179         var rangeEndRange = range.cloneRange();
22180         rangeEndRange.collapse(false);
22181     
22182         var nodeStartRange = nodeRange.cloneRange();
22183         nodeStartRange.collapse(true);
22184     
22185         var nodeEndRange = nodeRange.cloneRange();
22186         nodeEndRange.collapse(false);
22187     
22188         return rangeStartRange.compareBoundaryPoints(
22189                  Range.START_TO_START, nodeEndRange) == -1 &&
22190                rangeEndRange.compareBoundaryPoints(
22191                  Range.START_TO_START, nodeStartRange) == 1;
22192         
22193          
22194     },
22195     rangeCompareNode : function(range, node)
22196     {
22197         var nodeRange = node.ownerDocument.createRange();
22198         try {
22199             nodeRange.selectNode(node);
22200         } catch (e) {
22201             nodeRange.selectNodeContents(node);
22202         }
22203         
22204         
22205         range.collapse(true);
22206     
22207         nodeRange.collapse(true);
22208      
22209         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22210         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22211          
22212         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22213         
22214         var nodeIsBefore   =  ss == 1;
22215         var nodeIsAfter    = ee == -1;
22216         
22217         if (nodeIsBefore && nodeIsAfter) {
22218             return 0; // outer
22219         }
22220         if (!nodeIsBefore && nodeIsAfter) {
22221             return 1; //right trailed.
22222         }
22223         
22224         if (nodeIsBefore && !nodeIsAfter) {
22225             return 2;  // left trailed.
22226         }
22227         // fully contined.
22228         return 3;
22229     },
22230
22231     // private? - in a new class?
22232     cleanUpPaste :  function()
22233     {
22234         // cleans up the whole document..
22235         Roo.log('cleanuppaste');
22236         
22237         this.cleanUpChildren(this.doc.body);
22238         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22239         if (clean != this.doc.body.innerHTML) {
22240             this.doc.body.innerHTML = clean;
22241         }
22242         
22243     },
22244     
22245     cleanWordChars : function(input) {// change the chars to hex code
22246         var he = Roo.HtmlEditorCore;
22247         
22248         var output = input;
22249         Roo.each(he.swapCodes, function(sw) { 
22250             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22251             
22252             output = output.replace(swapper, sw[1]);
22253         });
22254         
22255         return output;
22256     },
22257     
22258     
22259     cleanUpChildren : function (n)
22260     {
22261         if (!n.childNodes.length) {
22262             return;
22263         }
22264         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22265            this.cleanUpChild(n.childNodes[i]);
22266         }
22267     },
22268     
22269     
22270         
22271     
22272     cleanUpChild : function (node)
22273     {
22274         var ed = this;
22275         //console.log(node);
22276         if (node.nodeName == "#text") {
22277             // clean up silly Windows -- stuff?
22278             return; 
22279         }
22280         if (node.nodeName == "#comment") {
22281             node.parentNode.removeChild(node);
22282             // clean up silly Windows -- stuff?
22283             return; 
22284         }
22285         var lcname = node.tagName.toLowerCase();
22286         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22287         // whitelist of tags..
22288         
22289         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22290             // remove node.
22291             node.parentNode.removeChild(node);
22292             return;
22293             
22294         }
22295         
22296         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22297         
22298         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22299         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22300         
22301         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22302         //    remove_keep_children = true;
22303         //}
22304         
22305         if (remove_keep_children) {
22306             this.cleanUpChildren(node);
22307             // inserts everything just before this node...
22308             while (node.childNodes.length) {
22309                 var cn = node.childNodes[0];
22310                 node.removeChild(cn);
22311                 node.parentNode.insertBefore(cn, node);
22312             }
22313             node.parentNode.removeChild(node);
22314             return;
22315         }
22316         
22317         if (!node.attributes || !node.attributes.length) {
22318             this.cleanUpChildren(node);
22319             return;
22320         }
22321         
22322         function cleanAttr(n,v)
22323         {
22324             
22325             if (v.match(/^\./) || v.match(/^\//)) {
22326                 return;
22327             }
22328             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22329                 return;
22330             }
22331             if (v.match(/^#/)) {
22332                 return;
22333             }
22334 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22335             node.removeAttribute(n);
22336             
22337         }
22338         
22339         var cwhite = this.cwhite;
22340         var cblack = this.cblack;
22341             
22342         function cleanStyle(n,v)
22343         {
22344             if (v.match(/expression/)) { //XSS?? should we even bother..
22345                 node.removeAttribute(n);
22346                 return;
22347             }
22348             
22349             var parts = v.split(/;/);
22350             var clean = [];
22351             
22352             Roo.each(parts, function(p) {
22353                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22354                 if (!p.length) {
22355                     return true;
22356                 }
22357                 var l = p.split(':').shift().replace(/\s+/g,'');
22358                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22359                 
22360                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22361 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22362                     //node.removeAttribute(n);
22363                     return true;
22364                 }
22365                 //Roo.log()
22366                 // only allow 'c whitelisted system attributes'
22367                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22368 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22369                     //node.removeAttribute(n);
22370                     return true;
22371                 }
22372                 
22373                 
22374                  
22375                 
22376                 clean.push(p);
22377                 return true;
22378             });
22379             if (clean.length) { 
22380                 node.setAttribute(n, clean.join(';'));
22381             } else {
22382                 node.removeAttribute(n);
22383             }
22384             
22385         }
22386         
22387         
22388         for (var i = node.attributes.length-1; i > -1 ; i--) {
22389             var a = node.attributes[i];
22390             //console.log(a);
22391             
22392             if (a.name.toLowerCase().substr(0,2)=='on')  {
22393                 node.removeAttribute(a.name);
22394                 continue;
22395             }
22396             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22397                 node.removeAttribute(a.name);
22398                 continue;
22399             }
22400             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22401                 cleanAttr(a.name,a.value); // fixme..
22402                 continue;
22403             }
22404             if (a.name == 'style') {
22405                 cleanStyle(a.name,a.value);
22406                 continue;
22407             }
22408             /// clean up MS crap..
22409             // tecnically this should be a list of valid class'es..
22410             
22411             
22412             if (a.name == 'class') {
22413                 if (a.value.match(/^Mso/)) {
22414                     node.className = '';
22415                 }
22416                 
22417                 if (a.value.match(/^body$/)) {
22418                     node.className = '';
22419                 }
22420                 continue;
22421             }
22422             
22423             // style cleanup!?
22424             // class cleanup?
22425             
22426         }
22427         
22428         
22429         this.cleanUpChildren(node);
22430         
22431         
22432     },
22433     
22434     /**
22435      * Clean up MS wordisms...
22436      */
22437     cleanWord : function(node)
22438     {
22439         
22440         
22441         if (!node) {
22442             this.cleanWord(this.doc.body);
22443             return;
22444         }
22445         if (node.nodeName == "#text") {
22446             // clean up silly Windows -- stuff?
22447             return; 
22448         }
22449         if (node.nodeName == "#comment") {
22450             node.parentNode.removeChild(node);
22451             // clean up silly Windows -- stuff?
22452             return; 
22453         }
22454         
22455         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22456             node.parentNode.removeChild(node);
22457             return;
22458         }
22459         
22460         // remove - but keep children..
22461         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22462             while (node.childNodes.length) {
22463                 var cn = node.childNodes[0];
22464                 node.removeChild(cn);
22465                 node.parentNode.insertBefore(cn, node);
22466             }
22467             node.parentNode.removeChild(node);
22468             this.iterateChildren(node, this.cleanWord);
22469             return;
22470         }
22471         // clean styles
22472         if (node.className.length) {
22473             
22474             var cn = node.className.split(/\W+/);
22475             var cna = [];
22476             Roo.each(cn, function(cls) {
22477                 if (cls.match(/Mso[a-zA-Z]+/)) {
22478                     return;
22479                 }
22480                 cna.push(cls);
22481             });
22482             node.className = cna.length ? cna.join(' ') : '';
22483             if (!cna.length) {
22484                 node.removeAttribute("class");
22485             }
22486         }
22487         
22488         if (node.hasAttribute("lang")) {
22489             node.removeAttribute("lang");
22490         }
22491         
22492         if (node.hasAttribute("style")) {
22493             
22494             var styles = node.getAttribute("style").split(";");
22495             var nstyle = [];
22496             Roo.each(styles, function(s) {
22497                 if (!s.match(/:/)) {
22498                     return;
22499                 }
22500                 var kv = s.split(":");
22501                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22502                     return;
22503                 }
22504                 // what ever is left... we allow.
22505                 nstyle.push(s);
22506             });
22507             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22508             if (!nstyle.length) {
22509                 node.removeAttribute('style');
22510             }
22511         }
22512         this.iterateChildren(node, this.cleanWord);
22513         
22514         
22515         
22516     },
22517     /**
22518      * iterateChildren of a Node, calling fn each time, using this as the scole..
22519      * @param {DomNode} node node to iterate children of.
22520      * @param {Function} fn method of this class to call on each item.
22521      */
22522     iterateChildren : function(node, fn)
22523     {
22524         if (!node.childNodes.length) {
22525                 return;
22526         }
22527         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22528            fn.call(this, node.childNodes[i])
22529         }
22530     },
22531     
22532     
22533     /**
22534      * cleanTableWidths.
22535      *
22536      * Quite often pasting from word etc.. results in tables with column and widths.
22537      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22538      *
22539      */
22540     cleanTableWidths : function(node)
22541     {
22542          
22543          
22544         if (!node) {
22545             this.cleanTableWidths(this.doc.body);
22546             return;
22547         }
22548         
22549         // ignore list...
22550         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22551             return; 
22552         }
22553         Roo.log(node.tagName);
22554         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22555             this.iterateChildren(node, this.cleanTableWidths);
22556             return;
22557         }
22558         if (node.hasAttribute('width')) {
22559             node.removeAttribute('width');
22560         }
22561         
22562          
22563         if (node.hasAttribute("style")) {
22564             // pretty basic...
22565             
22566             var styles = node.getAttribute("style").split(";");
22567             var nstyle = [];
22568             Roo.each(styles, function(s) {
22569                 if (!s.match(/:/)) {
22570                     return;
22571                 }
22572                 var kv = s.split(":");
22573                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22574                     return;
22575                 }
22576                 // what ever is left... we allow.
22577                 nstyle.push(s);
22578             });
22579             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22580             if (!nstyle.length) {
22581                 node.removeAttribute('style');
22582             }
22583         }
22584         
22585         this.iterateChildren(node, this.cleanTableWidths);
22586         
22587         
22588     },
22589     
22590     
22591     
22592     
22593     domToHTML : function(currentElement, depth, nopadtext) {
22594         
22595         depth = depth || 0;
22596         nopadtext = nopadtext || false;
22597     
22598         if (!currentElement) {
22599             return this.domToHTML(this.doc.body);
22600         }
22601         
22602         //Roo.log(currentElement);
22603         var j;
22604         var allText = false;
22605         var nodeName = currentElement.nodeName;
22606         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22607         
22608         if  (nodeName == '#text') {
22609             
22610             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22611         }
22612         
22613         
22614         var ret = '';
22615         if (nodeName != 'BODY') {
22616              
22617             var i = 0;
22618             // Prints the node tagName, such as <A>, <IMG>, etc
22619             if (tagName) {
22620                 var attr = [];
22621                 for(i = 0; i < currentElement.attributes.length;i++) {
22622                     // quoting?
22623                     var aname = currentElement.attributes.item(i).name;
22624                     if (!currentElement.attributes.item(i).value.length) {
22625                         continue;
22626                     }
22627                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22628                 }
22629                 
22630                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22631             } 
22632             else {
22633                 
22634                 // eack
22635             }
22636         } else {
22637             tagName = false;
22638         }
22639         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22640             return ret;
22641         }
22642         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22643             nopadtext = true;
22644         }
22645         
22646         
22647         // Traverse the tree
22648         i = 0;
22649         var currentElementChild = currentElement.childNodes.item(i);
22650         var allText = true;
22651         var innerHTML  = '';
22652         lastnode = '';
22653         while (currentElementChild) {
22654             // Formatting code (indent the tree so it looks nice on the screen)
22655             var nopad = nopadtext;
22656             if (lastnode == 'SPAN') {
22657                 nopad  = true;
22658             }
22659             // text
22660             if  (currentElementChild.nodeName == '#text') {
22661                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22662                 toadd = nopadtext ? toadd : toadd.trim();
22663                 if (!nopad && toadd.length > 80) {
22664                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22665                 }
22666                 innerHTML  += toadd;
22667                 
22668                 i++;
22669                 currentElementChild = currentElement.childNodes.item(i);
22670                 lastNode = '';
22671                 continue;
22672             }
22673             allText = false;
22674             
22675             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22676                 
22677             // Recursively traverse the tree structure of the child node
22678             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22679             lastnode = currentElementChild.nodeName;
22680             i++;
22681             currentElementChild=currentElement.childNodes.item(i);
22682         }
22683         
22684         ret += innerHTML;
22685         
22686         if (!allText) {
22687                 // The remaining code is mostly for formatting the tree
22688             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22689         }
22690         
22691         
22692         if (tagName) {
22693             ret+= "</"+tagName+">";
22694         }
22695         return ret;
22696         
22697     },
22698         
22699     applyBlacklists : function()
22700     {
22701         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22702         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22703         
22704         this.white = [];
22705         this.black = [];
22706         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22707             if (b.indexOf(tag) > -1) {
22708                 return;
22709             }
22710             this.white.push(tag);
22711             
22712         }, this);
22713         
22714         Roo.each(w, function(tag) {
22715             if (b.indexOf(tag) > -1) {
22716                 return;
22717             }
22718             if (this.white.indexOf(tag) > -1) {
22719                 return;
22720             }
22721             this.white.push(tag);
22722             
22723         }, this);
22724         
22725         
22726         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22727             if (w.indexOf(tag) > -1) {
22728                 return;
22729             }
22730             this.black.push(tag);
22731             
22732         }, this);
22733         
22734         Roo.each(b, function(tag) {
22735             if (w.indexOf(tag) > -1) {
22736                 return;
22737             }
22738             if (this.black.indexOf(tag) > -1) {
22739                 return;
22740             }
22741             this.black.push(tag);
22742             
22743         }, this);
22744         
22745         
22746         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22747         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22748         
22749         this.cwhite = [];
22750         this.cblack = [];
22751         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22752             if (b.indexOf(tag) > -1) {
22753                 return;
22754             }
22755             this.cwhite.push(tag);
22756             
22757         }, this);
22758         
22759         Roo.each(w, function(tag) {
22760             if (b.indexOf(tag) > -1) {
22761                 return;
22762             }
22763             if (this.cwhite.indexOf(tag) > -1) {
22764                 return;
22765             }
22766             this.cwhite.push(tag);
22767             
22768         }, this);
22769         
22770         
22771         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22772             if (w.indexOf(tag) > -1) {
22773                 return;
22774             }
22775             this.cblack.push(tag);
22776             
22777         }, this);
22778         
22779         Roo.each(b, function(tag) {
22780             if (w.indexOf(tag) > -1) {
22781                 return;
22782             }
22783             if (this.cblack.indexOf(tag) > -1) {
22784                 return;
22785             }
22786             this.cblack.push(tag);
22787             
22788         }, this);
22789     },
22790     
22791     setStylesheets : function(stylesheets)
22792     {
22793         if(typeof(stylesheets) == 'string'){
22794             Roo.get(this.iframe.contentDocument.head).createChild({
22795                 tag : 'link',
22796                 rel : 'stylesheet',
22797                 type : 'text/css',
22798                 href : stylesheets
22799             });
22800             
22801             return;
22802         }
22803         var _this = this;
22804      
22805         Roo.each(stylesheets, function(s) {
22806             if(!s.length){
22807                 return;
22808             }
22809             
22810             Roo.get(_this.iframe.contentDocument.head).createChild({
22811                 tag : 'link',
22812                 rel : 'stylesheet',
22813                 type : 'text/css',
22814                 href : s
22815             });
22816         });
22817
22818         
22819     },
22820     
22821     removeStylesheets : function()
22822     {
22823         var _this = this;
22824         
22825         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22826             s.remove();
22827         });
22828     },
22829     
22830     setStyle : function(style)
22831     {
22832         Roo.get(this.iframe.contentDocument.head).createChild({
22833             tag : 'style',
22834             type : 'text/css',
22835             html : style
22836         });
22837
22838         return;
22839     }
22840     
22841     // hide stuff that is not compatible
22842     /**
22843      * @event blur
22844      * @hide
22845      */
22846     /**
22847      * @event change
22848      * @hide
22849      */
22850     /**
22851      * @event focus
22852      * @hide
22853      */
22854     /**
22855      * @event specialkey
22856      * @hide
22857      */
22858     /**
22859      * @cfg {String} fieldClass @hide
22860      */
22861     /**
22862      * @cfg {String} focusClass @hide
22863      */
22864     /**
22865      * @cfg {String} autoCreate @hide
22866      */
22867     /**
22868      * @cfg {String} inputType @hide
22869      */
22870     /**
22871      * @cfg {String} invalidClass @hide
22872      */
22873     /**
22874      * @cfg {String} invalidText @hide
22875      */
22876     /**
22877      * @cfg {String} msgFx @hide
22878      */
22879     /**
22880      * @cfg {String} validateOnBlur @hide
22881      */
22882 });
22883
22884 Roo.HtmlEditorCore.white = [
22885         'area', 'br', 'img', 'input', 'hr', 'wbr',
22886         
22887        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22888        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22889        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22890        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22891        'table',   'ul',         'xmp', 
22892        
22893        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22894       'thead',   'tr', 
22895      
22896       'dir', 'menu', 'ol', 'ul', 'dl',
22897        
22898       'embed',  'object'
22899 ];
22900
22901
22902 Roo.HtmlEditorCore.black = [
22903     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22904         'applet', // 
22905         'base',   'basefont', 'bgsound', 'blink',  'body', 
22906         'frame',  'frameset', 'head',    'html',   'ilayer', 
22907         'iframe', 'layer',  'link',     'meta',    'object',   
22908         'script', 'style' ,'title',  'xml' // clean later..
22909 ];
22910 Roo.HtmlEditorCore.clean = [
22911     'script', 'style', 'title', 'xml'
22912 ];
22913 Roo.HtmlEditorCore.remove = [
22914     'font'
22915 ];
22916 // attributes..
22917
22918 Roo.HtmlEditorCore.ablack = [
22919     'on'
22920 ];
22921     
22922 Roo.HtmlEditorCore.aclean = [ 
22923     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22924 ];
22925
22926 // protocols..
22927 Roo.HtmlEditorCore.pwhite= [
22928         'http',  'https',  'mailto'
22929 ];
22930
22931 // white listed style attributes.
22932 Roo.HtmlEditorCore.cwhite= [
22933       //  'text-align', /// default is to allow most things..
22934       
22935          
22936 //        'font-size'//??
22937 ];
22938
22939 // black listed style attributes.
22940 Roo.HtmlEditorCore.cblack= [
22941       //  'font-size' -- this can be set by the project 
22942 ];
22943
22944
22945 Roo.HtmlEditorCore.swapCodes   =[ 
22946     [    8211, "--" ], 
22947     [    8212, "--" ], 
22948     [    8216,  "'" ],  
22949     [    8217, "'" ],  
22950     [    8220, '"' ],  
22951     [    8221, '"' ],  
22952     [    8226, "*" ],  
22953     [    8230, "..." ]
22954 ]; 
22955
22956     /*
22957  * - LGPL
22958  *
22959  * HtmlEditor
22960  * 
22961  */
22962
22963 /**
22964  * @class Roo.bootstrap.HtmlEditor
22965  * @extends Roo.bootstrap.TextArea
22966  * Bootstrap HtmlEditor class
22967
22968  * @constructor
22969  * Create a new HtmlEditor
22970  * @param {Object} config The config object
22971  */
22972
22973 Roo.bootstrap.HtmlEditor = function(config){
22974     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22975     if (!this.toolbars) {
22976         this.toolbars = [];
22977     }
22978     
22979     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22980     this.addEvents({
22981             /**
22982              * @event initialize
22983              * Fires when the editor is fully initialized (including the iframe)
22984              * @param {HtmlEditor} this
22985              */
22986             initialize: true,
22987             /**
22988              * @event activate
22989              * Fires when the editor is first receives the focus. Any insertion must wait
22990              * until after this event.
22991              * @param {HtmlEditor} this
22992              */
22993             activate: true,
22994              /**
22995              * @event beforesync
22996              * Fires before the textarea is updated with content from the editor iframe. Return false
22997              * to cancel the sync.
22998              * @param {HtmlEditor} this
22999              * @param {String} html
23000              */
23001             beforesync: true,
23002              /**
23003              * @event beforepush
23004              * Fires before the iframe editor is updated with content from the textarea. Return false
23005              * to cancel the push.
23006              * @param {HtmlEditor} this
23007              * @param {String} html
23008              */
23009             beforepush: true,
23010              /**
23011              * @event sync
23012              * Fires when the textarea is updated with content from the editor iframe.
23013              * @param {HtmlEditor} this
23014              * @param {String} html
23015              */
23016             sync: true,
23017              /**
23018              * @event push
23019              * Fires when the iframe editor is updated with content from the textarea.
23020              * @param {HtmlEditor} this
23021              * @param {String} html
23022              */
23023             push: true,
23024              /**
23025              * @event editmodechange
23026              * Fires when the editor switches edit modes
23027              * @param {HtmlEditor} this
23028              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23029              */
23030             editmodechange: true,
23031             /**
23032              * @event editorevent
23033              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23034              * @param {HtmlEditor} this
23035              */
23036             editorevent: true,
23037             /**
23038              * @event firstfocus
23039              * Fires when on first focus - needed by toolbars..
23040              * @param {HtmlEditor} this
23041              */
23042             firstfocus: true,
23043             /**
23044              * @event autosave
23045              * Auto save the htmlEditor value as a file into Events
23046              * @param {HtmlEditor} this
23047              */
23048             autosave: true,
23049             /**
23050              * @event savedpreview
23051              * preview the saved version of htmlEditor
23052              * @param {HtmlEditor} this
23053              */
23054             savedpreview: true
23055         });
23056 };
23057
23058
23059 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23060     
23061     
23062       /**
23063      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23064      */
23065     toolbars : false,
23066     
23067      /**
23068     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23069     */
23070     btns : [],
23071    
23072      /**
23073      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23074      *                        Roo.resizable.
23075      */
23076     resizable : false,
23077      /**
23078      * @cfg {Number} height (in pixels)
23079      */   
23080     height: 300,
23081    /**
23082      * @cfg {Number} width (in pixels)
23083      */   
23084     width: false,
23085     
23086     /**
23087      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23088      * 
23089      */
23090     stylesheets: false,
23091     
23092     // id of frame..
23093     frameId: false,
23094     
23095     // private properties
23096     validationEvent : false,
23097     deferHeight: true,
23098     initialized : false,
23099     activated : false,
23100     
23101     onFocus : Roo.emptyFn,
23102     iframePad:3,
23103     hideMode:'offsets',
23104     
23105     tbContainer : false,
23106     
23107     bodyCls : '',
23108     
23109     toolbarContainer :function() {
23110         return this.wrap.select('.x-html-editor-tb',true).first();
23111     },
23112
23113     /**
23114      * Protected method that will not generally be called directly. It
23115      * is called when the editor creates its toolbar. Override this method if you need to
23116      * add custom toolbar buttons.
23117      * @param {HtmlEditor} editor
23118      */
23119     createToolbar : function(){
23120         Roo.log('renewing');
23121         Roo.log("create toolbars");
23122         
23123         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23124         this.toolbars[0].render(this.toolbarContainer());
23125         
23126         return;
23127         
23128 //        if (!editor.toolbars || !editor.toolbars.length) {
23129 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23130 //        }
23131 //        
23132 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23133 //            editor.toolbars[i] = Roo.factory(
23134 //                    typeof(editor.toolbars[i]) == 'string' ?
23135 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23136 //                Roo.bootstrap.HtmlEditor);
23137 //            editor.toolbars[i].init(editor);
23138 //        }
23139     },
23140
23141      
23142     // private
23143     onRender : function(ct, position)
23144     {
23145        // Roo.log("Call onRender: " + this.xtype);
23146         var _t = this;
23147         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23148       
23149         this.wrap = this.inputEl().wrap({
23150             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23151         });
23152         
23153         this.editorcore.onRender(ct, position);
23154          
23155         if (this.resizable) {
23156             this.resizeEl = new Roo.Resizable(this.wrap, {
23157                 pinned : true,
23158                 wrap: true,
23159                 dynamic : true,
23160                 minHeight : this.height,
23161                 height: this.height,
23162                 handles : this.resizable,
23163                 width: this.width,
23164                 listeners : {
23165                     resize : function(r, w, h) {
23166                         _t.onResize(w,h); // -something
23167                     }
23168                 }
23169             });
23170             
23171         }
23172         this.createToolbar(this);
23173        
23174         
23175         if(!this.width && this.resizable){
23176             this.setSize(this.wrap.getSize());
23177         }
23178         if (this.resizeEl) {
23179             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23180             // should trigger onReize..
23181         }
23182         
23183     },
23184
23185     // private
23186     onResize : function(w, h)
23187     {
23188         Roo.log('resize: ' +w + ',' + h );
23189         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23190         var ew = false;
23191         var eh = false;
23192         
23193         if(this.inputEl() ){
23194             if(typeof w == 'number'){
23195                 var aw = w - this.wrap.getFrameWidth('lr');
23196                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23197                 ew = aw;
23198             }
23199             if(typeof h == 'number'){
23200                  var tbh = -11;  // fixme it needs to tool bar size!
23201                 for (var i =0; i < this.toolbars.length;i++) {
23202                     // fixme - ask toolbars for heights?
23203                     tbh += this.toolbars[i].el.getHeight();
23204                     //if (this.toolbars[i].footer) {
23205                     //    tbh += this.toolbars[i].footer.el.getHeight();
23206                     //}
23207                 }
23208               
23209                 
23210                 
23211                 
23212                 
23213                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23214                 ah -= 5; // knock a few pixes off for look..
23215                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23216                 var eh = ah;
23217             }
23218         }
23219         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23220         this.editorcore.onResize(ew,eh);
23221         
23222     },
23223
23224     /**
23225      * Toggles the editor between standard and source edit mode.
23226      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23227      */
23228     toggleSourceEdit : function(sourceEditMode)
23229     {
23230         this.editorcore.toggleSourceEdit(sourceEditMode);
23231         
23232         if(this.editorcore.sourceEditMode){
23233             Roo.log('editor - showing textarea');
23234             
23235 //            Roo.log('in');
23236 //            Roo.log(this.syncValue());
23237             this.syncValue();
23238             this.inputEl().removeClass(['hide', 'x-hidden']);
23239             this.inputEl().dom.removeAttribute('tabIndex');
23240             this.inputEl().focus();
23241         }else{
23242             Roo.log('editor - hiding textarea');
23243 //            Roo.log('out')
23244 //            Roo.log(this.pushValue()); 
23245             this.pushValue();
23246             
23247             this.inputEl().addClass(['hide', 'x-hidden']);
23248             this.inputEl().dom.setAttribute('tabIndex', -1);
23249             //this.deferFocus();
23250         }
23251          
23252         if(this.resizable){
23253             this.setSize(this.wrap.getSize());
23254         }
23255         
23256         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23257     },
23258  
23259     // private (for BoxComponent)
23260     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23261
23262     // private (for BoxComponent)
23263     getResizeEl : function(){
23264         return this.wrap;
23265     },
23266
23267     // private (for BoxComponent)
23268     getPositionEl : function(){
23269         return this.wrap;
23270     },
23271
23272     // private
23273     initEvents : function(){
23274         this.originalValue = this.getValue();
23275     },
23276
23277 //    /**
23278 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23279 //     * @method
23280 //     */
23281 //    markInvalid : Roo.emptyFn,
23282 //    /**
23283 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23284 //     * @method
23285 //     */
23286 //    clearInvalid : Roo.emptyFn,
23287
23288     setValue : function(v){
23289         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23290         this.editorcore.pushValue();
23291     },
23292
23293      
23294     // private
23295     deferFocus : function(){
23296         this.focus.defer(10, this);
23297     },
23298
23299     // doc'ed in Field
23300     focus : function(){
23301         this.editorcore.focus();
23302         
23303     },
23304       
23305
23306     // private
23307     onDestroy : function(){
23308         
23309         
23310         
23311         if(this.rendered){
23312             
23313             for (var i =0; i < this.toolbars.length;i++) {
23314                 // fixme - ask toolbars for heights?
23315                 this.toolbars[i].onDestroy();
23316             }
23317             
23318             this.wrap.dom.innerHTML = '';
23319             this.wrap.remove();
23320         }
23321     },
23322
23323     // private
23324     onFirstFocus : function(){
23325         //Roo.log("onFirstFocus");
23326         this.editorcore.onFirstFocus();
23327          for (var i =0; i < this.toolbars.length;i++) {
23328             this.toolbars[i].onFirstFocus();
23329         }
23330         
23331     },
23332     
23333     // private
23334     syncValue : function()
23335     {   
23336         this.editorcore.syncValue();
23337     },
23338     
23339     pushValue : function()
23340     {   
23341         this.editorcore.pushValue();
23342     }
23343      
23344     
23345     // hide stuff that is not compatible
23346     /**
23347      * @event blur
23348      * @hide
23349      */
23350     /**
23351      * @event change
23352      * @hide
23353      */
23354     /**
23355      * @event focus
23356      * @hide
23357      */
23358     /**
23359      * @event specialkey
23360      * @hide
23361      */
23362     /**
23363      * @cfg {String} fieldClass @hide
23364      */
23365     /**
23366      * @cfg {String} focusClass @hide
23367      */
23368     /**
23369      * @cfg {String} autoCreate @hide
23370      */
23371     /**
23372      * @cfg {String} inputType @hide
23373      */
23374     /**
23375      * @cfg {String} invalidClass @hide
23376      */
23377     /**
23378      * @cfg {String} invalidText @hide
23379      */
23380     /**
23381      * @cfg {String} msgFx @hide
23382      */
23383     /**
23384      * @cfg {String} validateOnBlur @hide
23385      */
23386 });
23387  
23388     
23389    
23390    
23391    
23392       
23393 Roo.namespace('Roo.bootstrap.htmleditor');
23394 /**
23395  * @class Roo.bootstrap.HtmlEditorToolbar1
23396  * Basic Toolbar
23397  * 
23398  * Usage:
23399  *
23400  new Roo.bootstrap.HtmlEditor({
23401     ....
23402     toolbars : [
23403         new Roo.bootstrap.HtmlEditorToolbar1({
23404             disable : { fonts: 1 , format: 1, ..., ... , ...],
23405             btns : [ .... ]
23406         })
23407     }
23408      
23409  * 
23410  * @cfg {Object} disable List of elements to disable..
23411  * @cfg {Array} btns List of additional buttons.
23412  * 
23413  * 
23414  * NEEDS Extra CSS? 
23415  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23416  */
23417  
23418 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23419 {
23420     
23421     Roo.apply(this, config);
23422     
23423     // default disabled, based on 'good practice'..
23424     this.disable = this.disable || {};
23425     Roo.applyIf(this.disable, {
23426         fontSize : true,
23427         colors : true,
23428         specialElements : true
23429     });
23430     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23431     
23432     this.editor = config.editor;
23433     this.editorcore = config.editor.editorcore;
23434     
23435     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23436     
23437     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23438     // dont call parent... till later.
23439 }
23440 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23441      
23442     bar : true,
23443     
23444     editor : false,
23445     editorcore : false,
23446     
23447     
23448     formats : [
23449         "p" ,  
23450         "h1","h2","h3","h4","h5","h6", 
23451         "pre", "code", 
23452         "abbr", "acronym", "address", "cite", "samp", "var",
23453         'div','span'
23454     ],
23455     
23456     onRender : function(ct, position)
23457     {
23458        // Roo.log("Call onRender: " + this.xtype);
23459         
23460        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23461        Roo.log(this.el);
23462        this.el.dom.style.marginBottom = '0';
23463        var _this = this;
23464        var editorcore = this.editorcore;
23465        var editor= this.editor;
23466        
23467        var children = [];
23468        var btn = function(id,cmd , toggle, handler, html){
23469        
23470             var  event = toggle ? 'toggle' : 'click';
23471        
23472             var a = {
23473                 size : 'sm',
23474                 xtype: 'Button',
23475                 xns: Roo.bootstrap,
23476                 glyphicon : id,
23477                 cmd : id || cmd,
23478                 enableToggle:toggle !== false,
23479                 html : html || '',
23480                 pressed : toggle ? false : null,
23481                 listeners : {}
23482             };
23483             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23484                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23485             };
23486             children.push(a);
23487             return a;
23488        }
23489        
23490     //    var cb_box = function...
23491         
23492         var style = {
23493                 xtype: 'Button',
23494                 size : 'sm',
23495                 xns: Roo.bootstrap,
23496                 glyphicon : 'font',
23497                 //html : 'submit'
23498                 menu : {
23499                     xtype: 'Menu',
23500                     xns: Roo.bootstrap,
23501                     items:  []
23502                 }
23503         };
23504         Roo.each(this.formats, function(f) {
23505             style.menu.items.push({
23506                 xtype :'MenuItem',
23507                 xns: Roo.bootstrap,
23508                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23509                 tagname : f,
23510                 listeners : {
23511                     click : function()
23512                     {
23513                         editorcore.insertTag(this.tagname);
23514                         editor.focus();
23515                     }
23516                 }
23517                 
23518             });
23519         });
23520         children.push(style);   
23521         
23522         btn('bold',false,true);
23523         btn('italic',false,true);
23524         btn('align-left', 'justifyleft',true);
23525         btn('align-center', 'justifycenter',true);
23526         btn('align-right' , 'justifyright',true);
23527         btn('link', false, false, function(btn) {
23528             //Roo.log("create link?");
23529             var url = prompt(this.createLinkText, this.defaultLinkValue);
23530             if(url && url != 'http:/'+'/'){
23531                 this.editorcore.relayCmd('createlink', url);
23532             }
23533         }),
23534         btn('list','insertunorderedlist',true);
23535         btn('pencil', false,true, function(btn){
23536                 Roo.log(this);
23537                 this.toggleSourceEdit(btn.pressed);
23538         });
23539         
23540         if (this.editor.btns.length > 0) {
23541             for (var i = 0; i<this.editor.btns.length; i++) {
23542                 children.push(this.editor.btns[i]);
23543             }
23544         }
23545         
23546         /*
23547         var cog = {
23548                 xtype: 'Button',
23549                 size : 'sm',
23550                 xns: Roo.bootstrap,
23551                 glyphicon : 'cog',
23552                 //html : 'submit'
23553                 menu : {
23554                     xtype: 'Menu',
23555                     xns: Roo.bootstrap,
23556                     items:  []
23557                 }
23558         };
23559         
23560         cog.menu.items.push({
23561             xtype :'MenuItem',
23562             xns: Roo.bootstrap,
23563             html : Clean styles,
23564             tagname : f,
23565             listeners : {
23566                 click : function()
23567                 {
23568                     editorcore.insertTag(this.tagname);
23569                     editor.focus();
23570                 }
23571             }
23572             
23573         });
23574        */
23575         
23576          
23577        this.xtype = 'NavSimplebar';
23578         
23579         for(var i=0;i< children.length;i++) {
23580             
23581             this.buttons.add(this.addxtypeChild(children[i]));
23582             
23583         }
23584         
23585         editor.on('editorevent', this.updateToolbar, this);
23586     },
23587     onBtnClick : function(id)
23588     {
23589        this.editorcore.relayCmd(id);
23590        this.editorcore.focus();
23591     },
23592     
23593     /**
23594      * Protected method that will not generally be called directly. It triggers
23595      * a toolbar update by reading the markup state of the current selection in the editor.
23596      */
23597     updateToolbar: function(){
23598
23599         if(!this.editorcore.activated){
23600             this.editor.onFirstFocus(); // is this neeed?
23601             return;
23602         }
23603
23604         var btns = this.buttons; 
23605         var doc = this.editorcore.doc;
23606         btns.get('bold').setActive(doc.queryCommandState('bold'));
23607         btns.get('italic').setActive(doc.queryCommandState('italic'));
23608         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23609         
23610         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23611         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23612         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23613         
23614         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23615         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23616          /*
23617         
23618         var ans = this.editorcore.getAllAncestors();
23619         if (this.formatCombo) {
23620             
23621             
23622             var store = this.formatCombo.store;
23623             this.formatCombo.setValue("");
23624             for (var i =0; i < ans.length;i++) {
23625                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23626                     // select it..
23627                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23628                     break;
23629                 }
23630             }
23631         }
23632         
23633         
23634         
23635         // hides menus... - so this cant be on a menu...
23636         Roo.bootstrap.MenuMgr.hideAll();
23637         */
23638         Roo.bootstrap.MenuMgr.hideAll();
23639         //this.editorsyncValue();
23640     },
23641     onFirstFocus: function() {
23642         this.buttons.each(function(item){
23643            item.enable();
23644         });
23645     },
23646     toggleSourceEdit : function(sourceEditMode){
23647         
23648           
23649         if(sourceEditMode){
23650             Roo.log("disabling buttons");
23651            this.buttons.each( function(item){
23652                 if(item.cmd != 'pencil'){
23653                     item.disable();
23654                 }
23655             });
23656           
23657         }else{
23658             Roo.log("enabling buttons");
23659             if(this.editorcore.initialized){
23660                 this.buttons.each( function(item){
23661                     item.enable();
23662                 });
23663             }
23664             
23665         }
23666         Roo.log("calling toggole on editor");
23667         // tell the editor that it's been pressed..
23668         this.editor.toggleSourceEdit(sourceEditMode);
23669        
23670     }
23671 });
23672
23673
23674
23675
23676
23677 /**
23678  * @class Roo.bootstrap.Table.AbstractSelectionModel
23679  * @extends Roo.util.Observable
23680  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23681  * implemented by descendant classes.  This class should not be directly instantiated.
23682  * @constructor
23683  */
23684 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23685     this.locked = false;
23686     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23687 };
23688
23689
23690 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23691     /** @ignore Called by the grid automatically. Do not call directly. */
23692     init : function(grid){
23693         this.grid = grid;
23694         this.initEvents();
23695     },
23696
23697     /**
23698      * Locks the selections.
23699      */
23700     lock : function(){
23701         this.locked = true;
23702     },
23703
23704     /**
23705      * Unlocks the selections.
23706      */
23707     unlock : function(){
23708         this.locked = false;
23709     },
23710
23711     /**
23712      * Returns true if the selections are locked.
23713      * @return {Boolean}
23714      */
23715     isLocked : function(){
23716         return this.locked;
23717     }
23718 });
23719 /**
23720  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23721  * @class Roo.bootstrap.Table.RowSelectionModel
23722  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23723  * It supports multiple selections and keyboard selection/navigation. 
23724  * @constructor
23725  * @param {Object} config
23726  */
23727
23728 Roo.bootstrap.Table.RowSelectionModel = function(config){
23729     Roo.apply(this, config);
23730     this.selections = new Roo.util.MixedCollection(false, function(o){
23731         return o.id;
23732     });
23733
23734     this.last = false;
23735     this.lastActive = false;
23736
23737     this.addEvents({
23738         /**
23739              * @event selectionchange
23740              * Fires when the selection changes
23741              * @param {SelectionModel} this
23742              */
23743             "selectionchange" : true,
23744         /**
23745              * @event afterselectionchange
23746              * Fires after the selection changes (eg. by key press or clicking)
23747              * @param {SelectionModel} this
23748              */
23749             "afterselectionchange" : true,
23750         /**
23751              * @event beforerowselect
23752              * Fires when a row is selected being selected, return false to cancel.
23753              * @param {SelectionModel} this
23754              * @param {Number} rowIndex The selected index
23755              * @param {Boolean} keepExisting False if other selections will be cleared
23756              */
23757             "beforerowselect" : true,
23758         /**
23759              * @event rowselect
23760              * Fires when a row is selected.
23761              * @param {SelectionModel} this
23762              * @param {Number} rowIndex The selected index
23763              * @param {Roo.data.Record} r The record
23764              */
23765             "rowselect" : true,
23766         /**
23767              * @event rowdeselect
23768              * Fires when a row is deselected.
23769              * @param {SelectionModel} this
23770              * @param {Number} rowIndex The selected index
23771              */
23772         "rowdeselect" : true
23773     });
23774     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23775     this.locked = false;
23776  };
23777
23778 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23779     /**
23780      * @cfg {Boolean} singleSelect
23781      * True to allow selection of only one row at a time (defaults to false)
23782      */
23783     singleSelect : false,
23784
23785     // private
23786     initEvents : function()
23787     {
23788
23789         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23790         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23791         //}else{ // allow click to work like normal
23792          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23793         //}
23794         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23795         this.grid.on("rowclick", this.handleMouseDown, this);
23796         
23797         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23798             "up" : function(e){
23799                 if(!e.shiftKey){
23800                     this.selectPrevious(e.shiftKey);
23801                 }else if(this.last !== false && this.lastActive !== false){
23802                     var last = this.last;
23803                     this.selectRange(this.last,  this.lastActive-1);
23804                     this.grid.getView().focusRow(this.lastActive);
23805                     if(last !== false){
23806                         this.last = last;
23807                     }
23808                 }else{
23809                     this.selectFirstRow();
23810                 }
23811                 this.fireEvent("afterselectionchange", this);
23812             },
23813             "down" : function(e){
23814                 if(!e.shiftKey){
23815                     this.selectNext(e.shiftKey);
23816                 }else if(this.last !== false && this.lastActive !== false){
23817                     var last = this.last;
23818                     this.selectRange(this.last,  this.lastActive+1);
23819                     this.grid.getView().focusRow(this.lastActive);
23820                     if(last !== false){
23821                         this.last = last;
23822                     }
23823                 }else{
23824                     this.selectFirstRow();
23825                 }
23826                 this.fireEvent("afterselectionchange", this);
23827             },
23828             scope: this
23829         });
23830         this.grid.store.on('load', function(){
23831             this.selections.clear();
23832         },this);
23833         /*
23834         var view = this.grid.view;
23835         view.on("refresh", this.onRefresh, this);
23836         view.on("rowupdated", this.onRowUpdated, this);
23837         view.on("rowremoved", this.onRemove, this);
23838         */
23839     },
23840
23841     // private
23842     onRefresh : function()
23843     {
23844         var ds = this.grid.store, i, v = this.grid.view;
23845         var s = this.selections;
23846         s.each(function(r){
23847             if((i = ds.indexOfId(r.id)) != -1){
23848                 v.onRowSelect(i);
23849             }else{
23850                 s.remove(r);
23851             }
23852         });
23853     },
23854
23855     // private
23856     onRemove : function(v, index, r){
23857         this.selections.remove(r);
23858     },
23859
23860     // private
23861     onRowUpdated : function(v, index, r){
23862         if(this.isSelected(r)){
23863             v.onRowSelect(index);
23864         }
23865     },
23866
23867     /**
23868      * Select records.
23869      * @param {Array} records The records to select
23870      * @param {Boolean} keepExisting (optional) True to keep existing selections
23871      */
23872     selectRecords : function(records, keepExisting)
23873     {
23874         if(!keepExisting){
23875             this.clearSelections();
23876         }
23877             var ds = this.grid.store;
23878         for(var i = 0, len = records.length; i < len; i++){
23879             this.selectRow(ds.indexOf(records[i]), true);
23880         }
23881     },
23882
23883     /**
23884      * Gets the number of selected rows.
23885      * @return {Number}
23886      */
23887     getCount : function(){
23888         return this.selections.length;
23889     },
23890
23891     /**
23892      * Selects the first row in the grid.
23893      */
23894     selectFirstRow : function(){
23895         this.selectRow(0);
23896     },
23897
23898     /**
23899      * Select the last row.
23900      * @param {Boolean} keepExisting (optional) True to keep existing selections
23901      */
23902     selectLastRow : function(keepExisting){
23903         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23904         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23905     },
23906
23907     /**
23908      * Selects the row immediately following the last selected row.
23909      * @param {Boolean} keepExisting (optional) True to keep existing selections
23910      */
23911     selectNext : function(keepExisting)
23912     {
23913             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23914             this.selectRow(this.last+1, keepExisting);
23915             this.grid.getView().focusRow(this.last);
23916         }
23917     },
23918
23919     /**
23920      * Selects the row that precedes the last selected row.
23921      * @param {Boolean} keepExisting (optional) True to keep existing selections
23922      */
23923     selectPrevious : function(keepExisting){
23924         if(this.last){
23925             this.selectRow(this.last-1, keepExisting);
23926             this.grid.getView().focusRow(this.last);
23927         }
23928     },
23929
23930     /**
23931      * Returns the selected records
23932      * @return {Array} Array of selected records
23933      */
23934     getSelections : function(){
23935         return [].concat(this.selections.items);
23936     },
23937
23938     /**
23939      * Returns the first selected record.
23940      * @return {Record}
23941      */
23942     getSelected : function(){
23943         return this.selections.itemAt(0);
23944     },
23945
23946
23947     /**
23948      * Clears all selections.
23949      */
23950     clearSelections : function(fast)
23951     {
23952         if(this.locked) {
23953             return;
23954         }
23955         if(fast !== true){
23956                 var ds = this.grid.store;
23957             var s = this.selections;
23958             s.each(function(r){
23959                 this.deselectRow(ds.indexOfId(r.id));
23960             }, this);
23961             s.clear();
23962         }else{
23963             this.selections.clear();
23964         }
23965         this.last = false;
23966     },
23967
23968
23969     /**
23970      * Selects all rows.
23971      */
23972     selectAll : function(){
23973         if(this.locked) {
23974             return;
23975         }
23976         this.selections.clear();
23977         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23978             this.selectRow(i, true);
23979         }
23980     },
23981
23982     /**
23983      * Returns True if there is a selection.
23984      * @return {Boolean}
23985      */
23986     hasSelection : function(){
23987         return this.selections.length > 0;
23988     },
23989
23990     /**
23991      * Returns True if the specified row is selected.
23992      * @param {Number/Record} record The record or index of the record to check
23993      * @return {Boolean}
23994      */
23995     isSelected : function(index){
23996             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23997         return (r && this.selections.key(r.id) ? true : false);
23998     },
23999
24000     /**
24001      * Returns True if the specified record id is selected.
24002      * @param {String} id The id of record to check
24003      * @return {Boolean}
24004      */
24005     isIdSelected : function(id){
24006         return (this.selections.key(id) ? true : false);
24007     },
24008
24009
24010     // private
24011     handleMouseDBClick : function(e, t){
24012         
24013     },
24014     // private
24015     handleMouseDown : function(e, t)
24016     {
24017             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24018         if(this.isLocked() || rowIndex < 0 ){
24019             return;
24020         };
24021         if(e.shiftKey && this.last !== false){
24022             var last = this.last;
24023             this.selectRange(last, rowIndex, e.ctrlKey);
24024             this.last = last; // reset the last
24025             t.focus();
24026     
24027         }else{
24028             var isSelected = this.isSelected(rowIndex);
24029             //Roo.log("select row:" + rowIndex);
24030             if(isSelected){
24031                 this.deselectRow(rowIndex);
24032             } else {
24033                         this.selectRow(rowIndex, true);
24034             }
24035     
24036             /*
24037                 if(e.button !== 0 && isSelected){
24038                 alert('rowIndex 2: ' + rowIndex);
24039                     view.focusRow(rowIndex);
24040                 }else if(e.ctrlKey && isSelected){
24041                     this.deselectRow(rowIndex);
24042                 }else if(!isSelected){
24043                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24044                     view.focusRow(rowIndex);
24045                 }
24046             */
24047         }
24048         this.fireEvent("afterselectionchange", this);
24049     },
24050     // private
24051     handleDragableRowClick :  function(grid, rowIndex, e) 
24052     {
24053         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24054             this.selectRow(rowIndex, false);
24055             grid.view.focusRow(rowIndex);
24056              this.fireEvent("afterselectionchange", this);
24057         }
24058     },
24059     
24060     /**
24061      * Selects multiple rows.
24062      * @param {Array} rows Array of the indexes of the row to select
24063      * @param {Boolean} keepExisting (optional) True to keep existing selections
24064      */
24065     selectRows : function(rows, keepExisting){
24066         if(!keepExisting){
24067             this.clearSelections();
24068         }
24069         for(var i = 0, len = rows.length; i < len; i++){
24070             this.selectRow(rows[i], true);
24071         }
24072     },
24073
24074     /**
24075      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24076      * @param {Number} startRow The index of the first row in the range
24077      * @param {Number} endRow The index of the last row in the range
24078      * @param {Boolean} keepExisting (optional) True to retain existing selections
24079      */
24080     selectRange : function(startRow, endRow, keepExisting){
24081         if(this.locked) {
24082             return;
24083         }
24084         if(!keepExisting){
24085             this.clearSelections();
24086         }
24087         if(startRow <= endRow){
24088             for(var i = startRow; i <= endRow; i++){
24089                 this.selectRow(i, true);
24090             }
24091         }else{
24092             for(var i = startRow; i >= endRow; i--){
24093                 this.selectRow(i, true);
24094             }
24095         }
24096     },
24097
24098     /**
24099      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24100      * @param {Number} startRow The index of the first row in the range
24101      * @param {Number} endRow The index of the last row in the range
24102      */
24103     deselectRange : function(startRow, endRow, preventViewNotify){
24104         if(this.locked) {
24105             return;
24106         }
24107         for(var i = startRow; i <= endRow; i++){
24108             this.deselectRow(i, preventViewNotify);
24109         }
24110     },
24111
24112     /**
24113      * Selects a row.
24114      * @param {Number} row The index of the row to select
24115      * @param {Boolean} keepExisting (optional) True to keep existing selections
24116      */
24117     selectRow : function(index, keepExisting, preventViewNotify)
24118     {
24119             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24120             return;
24121         }
24122         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24123             if(!keepExisting || this.singleSelect){
24124                 this.clearSelections();
24125             }
24126             
24127             var r = this.grid.store.getAt(index);
24128             //console.log('selectRow - record id :' + r.id);
24129             
24130             this.selections.add(r);
24131             this.last = this.lastActive = index;
24132             if(!preventViewNotify){
24133                 var proxy = new Roo.Element(
24134                                 this.grid.getRowDom(index)
24135                 );
24136                 proxy.addClass('bg-info info');
24137             }
24138             this.fireEvent("rowselect", this, index, r);
24139             this.fireEvent("selectionchange", this);
24140         }
24141     },
24142
24143     /**
24144      * Deselects a row.
24145      * @param {Number} row The index of the row to deselect
24146      */
24147     deselectRow : function(index, preventViewNotify)
24148     {
24149         if(this.locked) {
24150             return;
24151         }
24152         if(this.last == index){
24153             this.last = false;
24154         }
24155         if(this.lastActive == index){
24156             this.lastActive = false;
24157         }
24158         
24159         var r = this.grid.store.getAt(index);
24160         if (!r) {
24161             return;
24162         }
24163         
24164         this.selections.remove(r);
24165         //.console.log('deselectRow - record id :' + r.id);
24166         if(!preventViewNotify){
24167         
24168             var proxy = new Roo.Element(
24169                 this.grid.getRowDom(index)
24170             );
24171             proxy.removeClass('bg-info info');
24172         }
24173         this.fireEvent("rowdeselect", this, index);
24174         this.fireEvent("selectionchange", this);
24175     },
24176
24177     // private
24178     restoreLast : function(){
24179         if(this._last){
24180             this.last = this._last;
24181         }
24182     },
24183
24184     // private
24185     acceptsNav : function(row, col, cm){
24186         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24187     },
24188
24189     // private
24190     onEditorKey : function(field, e){
24191         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24192         if(k == e.TAB){
24193             e.stopEvent();
24194             ed.completeEdit();
24195             if(e.shiftKey){
24196                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24197             }else{
24198                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24199             }
24200         }else if(k == e.ENTER && !e.ctrlKey){
24201             e.stopEvent();
24202             ed.completeEdit();
24203             if(e.shiftKey){
24204                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24205             }else{
24206                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24207             }
24208         }else if(k == e.ESC){
24209             ed.cancelEdit();
24210         }
24211         if(newCell){
24212             g.startEditing(newCell[0], newCell[1]);
24213         }
24214     }
24215 });
24216 /*
24217  * Based on:
24218  * Ext JS Library 1.1.1
24219  * Copyright(c) 2006-2007, Ext JS, LLC.
24220  *
24221  * Originally Released Under LGPL - original licence link has changed is not relivant.
24222  *
24223  * Fork - LGPL
24224  * <script type="text/javascript">
24225  */
24226  
24227 /**
24228  * @class Roo.bootstrap.PagingToolbar
24229  * @extends Roo.bootstrap.NavSimplebar
24230  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24231  * @constructor
24232  * Create a new PagingToolbar
24233  * @param {Object} config The config object
24234  * @param {Roo.data.Store} store
24235  */
24236 Roo.bootstrap.PagingToolbar = function(config)
24237 {
24238     // old args format still supported... - xtype is prefered..
24239         // created from xtype...
24240     
24241     this.ds = config.dataSource;
24242     
24243     if (config.store && !this.ds) {
24244         this.store= Roo.factory(config.store, Roo.data);
24245         this.ds = this.store;
24246         this.ds.xmodule = this.xmodule || false;
24247     }
24248     
24249     this.toolbarItems = [];
24250     if (config.items) {
24251         this.toolbarItems = config.items;
24252     }
24253     
24254     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24255     
24256     this.cursor = 0;
24257     
24258     if (this.ds) { 
24259         this.bind(this.ds);
24260     }
24261     
24262     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24263     
24264 };
24265
24266 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24267     /**
24268      * @cfg {Roo.data.Store} dataSource
24269      * The underlying data store providing the paged data
24270      */
24271     /**
24272      * @cfg {String/HTMLElement/Element} container
24273      * container The id or element that will contain the toolbar
24274      */
24275     /**
24276      * @cfg {Boolean} displayInfo
24277      * True to display the displayMsg (defaults to false)
24278      */
24279     /**
24280      * @cfg {Number} pageSize
24281      * The number of records to display per page (defaults to 20)
24282      */
24283     pageSize: 20,
24284     /**
24285      * @cfg {String} displayMsg
24286      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24287      */
24288     displayMsg : 'Displaying {0} - {1} of {2}',
24289     /**
24290      * @cfg {String} emptyMsg
24291      * The message to display when no records are found (defaults to "No data to display")
24292      */
24293     emptyMsg : 'No data to display',
24294     /**
24295      * Customizable piece of the default paging text (defaults to "Page")
24296      * @type String
24297      */
24298     beforePageText : "Page",
24299     /**
24300      * Customizable piece of the default paging text (defaults to "of %0")
24301      * @type String
24302      */
24303     afterPageText : "of {0}",
24304     /**
24305      * Customizable piece of the default paging text (defaults to "First Page")
24306      * @type String
24307      */
24308     firstText : "First Page",
24309     /**
24310      * Customizable piece of the default paging text (defaults to "Previous Page")
24311      * @type String
24312      */
24313     prevText : "Previous Page",
24314     /**
24315      * Customizable piece of the default paging text (defaults to "Next Page")
24316      * @type String
24317      */
24318     nextText : "Next Page",
24319     /**
24320      * Customizable piece of the default paging text (defaults to "Last Page")
24321      * @type String
24322      */
24323     lastText : "Last Page",
24324     /**
24325      * Customizable piece of the default paging text (defaults to "Refresh")
24326      * @type String
24327      */
24328     refreshText : "Refresh",
24329
24330     buttons : false,
24331     // private
24332     onRender : function(ct, position) 
24333     {
24334         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24335         this.navgroup.parentId = this.id;
24336         this.navgroup.onRender(this.el, null);
24337         // add the buttons to the navgroup
24338         
24339         if(this.displayInfo){
24340             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24341             this.displayEl = this.el.select('.x-paging-info', true).first();
24342 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24343 //            this.displayEl = navel.el.select('span',true).first();
24344         }
24345         
24346         var _this = this;
24347         
24348         if(this.buttons){
24349             Roo.each(_this.buttons, function(e){ // this might need to use render????
24350                Roo.factory(e).onRender(_this.el, null);
24351             });
24352         }
24353             
24354         Roo.each(_this.toolbarItems, function(e) {
24355             _this.navgroup.addItem(e);
24356         });
24357         
24358         
24359         this.first = this.navgroup.addItem({
24360             tooltip: this.firstText,
24361             cls: "prev",
24362             icon : 'fa fa-backward',
24363             disabled: true,
24364             preventDefault: true,
24365             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24366         });
24367         
24368         this.prev =  this.navgroup.addItem({
24369             tooltip: this.prevText,
24370             cls: "prev",
24371             icon : 'fa fa-step-backward',
24372             disabled: true,
24373             preventDefault: true,
24374             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24375         });
24376     //this.addSeparator();
24377         
24378         
24379         var field = this.navgroup.addItem( {
24380             tagtype : 'span',
24381             cls : 'x-paging-position',
24382             
24383             html : this.beforePageText  +
24384                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24385                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24386          } ); //?? escaped?
24387         
24388         this.field = field.el.select('input', true).first();
24389         this.field.on("keydown", this.onPagingKeydown, this);
24390         this.field.on("focus", function(){this.dom.select();});
24391     
24392     
24393         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24394         //this.field.setHeight(18);
24395         //this.addSeparator();
24396         this.next = this.navgroup.addItem({
24397             tooltip: this.nextText,
24398             cls: "next",
24399             html : ' <i class="fa fa-step-forward">',
24400             disabled: true,
24401             preventDefault: true,
24402             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24403         });
24404         this.last = this.navgroup.addItem({
24405             tooltip: this.lastText,
24406             icon : 'fa fa-forward',
24407             cls: "next",
24408             disabled: true,
24409             preventDefault: true,
24410             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24411         });
24412     //this.addSeparator();
24413         this.loading = this.navgroup.addItem({
24414             tooltip: this.refreshText,
24415             icon: 'fa fa-refresh',
24416             preventDefault: true,
24417             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24418         });
24419         
24420     },
24421
24422     // private
24423     updateInfo : function(){
24424         if(this.displayEl){
24425             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24426             var msg = count == 0 ?
24427                 this.emptyMsg :
24428                 String.format(
24429                     this.displayMsg,
24430                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24431                 );
24432             this.displayEl.update(msg);
24433         }
24434     },
24435
24436     // private
24437     onLoad : function(ds, r, o)
24438     {
24439         this.cursor = o.params.start ? o.params.start : 0;
24440         
24441         var d = this.getPageData(),
24442             ap = d.activePage,
24443             ps = d.pages;
24444         
24445         
24446         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24447         this.field.dom.value = ap;
24448         this.first.setDisabled(ap == 1);
24449         this.prev.setDisabled(ap == 1);
24450         this.next.setDisabled(ap == ps);
24451         this.last.setDisabled(ap == ps);
24452         this.loading.enable();
24453         this.updateInfo();
24454     },
24455
24456     // private
24457     getPageData : function(){
24458         var total = this.ds.getTotalCount();
24459         return {
24460             total : total,
24461             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24462             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24463         };
24464     },
24465
24466     // private
24467     onLoadError : function(){
24468         this.loading.enable();
24469     },
24470
24471     // private
24472     onPagingKeydown : function(e){
24473         var k = e.getKey();
24474         var d = this.getPageData();
24475         if(k == e.RETURN){
24476             var v = this.field.dom.value, pageNum;
24477             if(!v || isNaN(pageNum = parseInt(v, 10))){
24478                 this.field.dom.value = d.activePage;
24479                 return;
24480             }
24481             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24482             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24483             e.stopEvent();
24484         }
24485         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))
24486         {
24487           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24488           this.field.dom.value = pageNum;
24489           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24490           e.stopEvent();
24491         }
24492         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24493         {
24494           var v = this.field.dom.value, pageNum; 
24495           var increment = (e.shiftKey) ? 10 : 1;
24496           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24497                 increment *= -1;
24498           }
24499           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24500             this.field.dom.value = d.activePage;
24501             return;
24502           }
24503           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24504           {
24505             this.field.dom.value = parseInt(v, 10) + increment;
24506             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24507             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24508           }
24509           e.stopEvent();
24510         }
24511     },
24512
24513     // private
24514     beforeLoad : function(){
24515         if(this.loading){
24516             this.loading.disable();
24517         }
24518     },
24519
24520     // private
24521     onClick : function(which){
24522         
24523         var ds = this.ds;
24524         if (!ds) {
24525             return;
24526         }
24527         
24528         switch(which){
24529             case "first":
24530                 ds.load({params:{start: 0, limit: this.pageSize}});
24531             break;
24532             case "prev":
24533                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24534             break;
24535             case "next":
24536                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24537             break;
24538             case "last":
24539                 var total = ds.getTotalCount();
24540                 var extra = total % this.pageSize;
24541                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24542                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24543             break;
24544             case "refresh":
24545                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24546             break;
24547         }
24548     },
24549
24550     /**
24551      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24552      * @param {Roo.data.Store} store The data store to unbind
24553      */
24554     unbind : function(ds){
24555         ds.un("beforeload", this.beforeLoad, this);
24556         ds.un("load", this.onLoad, this);
24557         ds.un("loadexception", this.onLoadError, this);
24558         ds.un("remove", this.updateInfo, this);
24559         ds.un("add", this.updateInfo, this);
24560         this.ds = undefined;
24561     },
24562
24563     /**
24564      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24565      * @param {Roo.data.Store} store The data store to bind
24566      */
24567     bind : function(ds){
24568         ds.on("beforeload", this.beforeLoad, this);
24569         ds.on("load", this.onLoad, this);
24570         ds.on("loadexception", this.onLoadError, this);
24571         ds.on("remove", this.updateInfo, this);
24572         ds.on("add", this.updateInfo, this);
24573         this.ds = ds;
24574     }
24575 });/*
24576  * - LGPL
24577  *
24578  * element
24579  * 
24580  */
24581
24582 /**
24583  * @class Roo.bootstrap.MessageBar
24584  * @extends Roo.bootstrap.Component
24585  * Bootstrap MessageBar class
24586  * @cfg {String} html contents of the MessageBar
24587  * @cfg {String} weight (info | success | warning | danger) default info
24588  * @cfg {String} beforeClass insert the bar before the given class
24589  * @cfg {Boolean} closable (true | false) default false
24590  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24591  * 
24592  * @constructor
24593  * Create a new Element
24594  * @param {Object} config The config object
24595  */
24596
24597 Roo.bootstrap.MessageBar = function(config){
24598     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24599 };
24600
24601 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24602     
24603     html: '',
24604     weight: 'info',
24605     closable: false,
24606     fixed: false,
24607     beforeClass: 'bootstrap-sticky-wrap',
24608     
24609     getAutoCreate : function(){
24610         
24611         var cfg = {
24612             tag: 'div',
24613             cls: 'alert alert-dismissable alert-' + this.weight,
24614             cn: [
24615                 {
24616                     tag: 'span',
24617                     cls: 'message',
24618                     html: this.html || ''
24619                 }
24620             ]
24621         };
24622         
24623         if(this.fixed){
24624             cfg.cls += ' alert-messages-fixed';
24625         }
24626         
24627         if(this.closable){
24628             cfg.cn.push({
24629                 tag: 'button',
24630                 cls: 'close',
24631                 html: 'x'
24632             });
24633         }
24634         
24635         return cfg;
24636     },
24637     
24638     onRender : function(ct, position)
24639     {
24640         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24641         
24642         if(!this.el){
24643             var cfg = Roo.apply({},  this.getAutoCreate());
24644             cfg.id = Roo.id();
24645             
24646             if (this.cls) {
24647                 cfg.cls += ' ' + this.cls;
24648             }
24649             if (this.style) {
24650                 cfg.style = this.style;
24651             }
24652             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24653             
24654             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24655         }
24656         
24657         this.el.select('>button.close').on('click', this.hide, this);
24658         
24659     },
24660     
24661     show : function()
24662     {
24663         if (!this.rendered) {
24664             this.render();
24665         }
24666         
24667         this.el.show();
24668         
24669         this.fireEvent('show', this);
24670         
24671     },
24672     
24673     hide : function()
24674     {
24675         if (!this.rendered) {
24676             this.render();
24677         }
24678         
24679         this.el.hide();
24680         
24681         this.fireEvent('hide', this);
24682     },
24683     
24684     update : function()
24685     {
24686 //        var e = this.el.dom.firstChild;
24687 //        
24688 //        if(this.closable){
24689 //            e = e.nextSibling;
24690 //        }
24691 //        
24692 //        e.data = this.html || '';
24693
24694         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24695     }
24696    
24697 });
24698
24699  
24700
24701      /*
24702  * - LGPL
24703  *
24704  * Graph
24705  * 
24706  */
24707
24708
24709 /**
24710  * @class Roo.bootstrap.Graph
24711  * @extends Roo.bootstrap.Component
24712  * Bootstrap Graph class
24713 > Prameters
24714  -sm {number} sm 4
24715  -md {number} md 5
24716  @cfg {String} graphtype  bar | vbar | pie
24717  @cfg {number} g_x coodinator | centre x (pie)
24718  @cfg {number} g_y coodinator | centre y (pie)
24719  @cfg {number} g_r radius (pie)
24720  @cfg {number} g_height height of the chart (respected by all elements in the set)
24721  @cfg {number} g_width width of the chart (respected by all elements in the set)
24722  @cfg {Object} title The title of the chart
24723     
24724  -{Array}  values
24725  -opts (object) options for the chart 
24726      o {
24727      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24728      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24729      o vgutter (number)
24730      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.
24731      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24732      o to
24733      o stretch (boolean)
24734      o }
24735  -opts (object) options for the pie
24736      o{
24737      o cut
24738      o startAngle (number)
24739      o endAngle (number)
24740      } 
24741  *
24742  * @constructor
24743  * Create a new Input
24744  * @param {Object} config The config object
24745  */
24746
24747 Roo.bootstrap.Graph = function(config){
24748     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24749     
24750     this.addEvents({
24751         // img events
24752         /**
24753          * @event click
24754          * The img click event for the img.
24755          * @param {Roo.EventObject} e
24756          */
24757         "click" : true
24758     });
24759 };
24760
24761 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24762     
24763     sm: 4,
24764     md: 5,
24765     graphtype: 'bar',
24766     g_height: 250,
24767     g_width: 400,
24768     g_x: 50,
24769     g_y: 50,
24770     g_r: 30,
24771     opts:{
24772         //g_colors: this.colors,
24773         g_type: 'soft',
24774         g_gutter: '20%'
24775
24776     },
24777     title : false,
24778
24779     getAutoCreate : function(){
24780         
24781         var cfg = {
24782             tag: 'div',
24783             html : null
24784         };
24785         
24786         
24787         return  cfg;
24788     },
24789
24790     onRender : function(ct,position){
24791         
24792         
24793         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24794         
24795         if (typeof(Raphael) == 'undefined') {
24796             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24797             return;
24798         }
24799         
24800         this.raphael = Raphael(this.el.dom);
24801         
24802                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24803                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24804                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24805                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24806                 /*
24807                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24808                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24809                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24810                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24811                 
24812                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24813                 r.barchart(330, 10, 300, 220, data1);
24814                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24815                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24816                 */
24817                 
24818                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24819                 // r.barchart(30, 30, 560, 250,  xdata, {
24820                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24821                 //     axis : "0 0 1 1",
24822                 //     axisxlabels :  xdata
24823                 //     //yvalues : cols,
24824                    
24825                 // });
24826 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24827 //        
24828 //        this.load(null,xdata,{
24829 //                axis : "0 0 1 1",
24830 //                axisxlabels :  xdata
24831 //                });
24832
24833     },
24834
24835     load : function(graphtype,xdata,opts)
24836     {
24837         this.raphael.clear();
24838         if(!graphtype) {
24839             graphtype = this.graphtype;
24840         }
24841         if(!opts){
24842             opts = this.opts;
24843         }
24844         var r = this.raphael,
24845             fin = function () {
24846                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24847             },
24848             fout = function () {
24849                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24850             },
24851             pfin = function() {
24852                 this.sector.stop();
24853                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24854
24855                 if (this.label) {
24856                     this.label[0].stop();
24857                     this.label[0].attr({ r: 7.5 });
24858                     this.label[1].attr({ "font-weight": 800 });
24859                 }
24860             },
24861             pfout = function() {
24862                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24863
24864                 if (this.label) {
24865                     this.label[0].animate({ r: 5 }, 500, "bounce");
24866                     this.label[1].attr({ "font-weight": 400 });
24867                 }
24868             };
24869
24870         switch(graphtype){
24871             case 'bar':
24872                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24873                 break;
24874             case 'hbar':
24875                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24876                 break;
24877             case 'pie':
24878 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24879 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24880 //            
24881                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24882                 
24883                 break;
24884
24885         }
24886         
24887         if(this.title){
24888             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24889         }
24890         
24891     },
24892     
24893     setTitle: function(o)
24894     {
24895         this.title = o;
24896     },
24897     
24898     initEvents: function() {
24899         
24900         if(!this.href){
24901             this.el.on('click', this.onClick, this);
24902         }
24903     },
24904     
24905     onClick : function(e)
24906     {
24907         Roo.log('img onclick');
24908         this.fireEvent('click', this, e);
24909     }
24910    
24911 });
24912
24913  
24914 /*
24915  * - LGPL
24916  *
24917  * numberBox
24918  * 
24919  */
24920 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24921
24922 /**
24923  * @class Roo.bootstrap.dash.NumberBox
24924  * @extends Roo.bootstrap.Component
24925  * Bootstrap NumberBox class
24926  * @cfg {String} headline Box headline
24927  * @cfg {String} content Box content
24928  * @cfg {String} icon Box icon
24929  * @cfg {String} footer Footer text
24930  * @cfg {String} fhref Footer href
24931  * 
24932  * @constructor
24933  * Create a new NumberBox
24934  * @param {Object} config The config object
24935  */
24936
24937
24938 Roo.bootstrap.dash.NumberBox = function(config){
24939     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24940     
24941 };
24942
24943 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24944     
24945     headline : '',
24946     content : '',
24947     icon : '',
24948     footer : '',
24949     fhref : '',
24950     ficon : '',
24951     
24952     getAutoCreate : function(){
24953         
24954         var cfg = {
24955             tag : 'div',
24956             cls : 'small-box ',
24957             cn : [
24958                 {
24959                     tag : 'div',
24960                     cls : 'inner',
24961                     cn :[
24962                         {
24963                             tag : 'h3',
24964                             cls : 'roo-headline',
24965                             html : this.headline
24966                         },
24967                         {
24968                             tag : 'p',
24969                             cls : 'roo-content',
24970                             html : this.content
24971                         }
24972                     ]
24973                 }
24974             ]
24975         };
24976         
24977         if(this.icon){
24978             cfg.cn.push({
24979                 tag : 'div',
24980                 cls : 'icon',
24981                 cn :[
24982                     {
24983                         tag : 'i',
24984                         cls : 'ion ' + this.icon
24985                     }
24986                 ]
24987             });
24988         }
24989         
24990         if(this.footer){
24991             var footer = {
24992                 tag : 'a',
24993                 cls : 'small-box-footer',
24994                 href : this.fhref || '#',
24995                 html : this.footer
24996             };
24997             
24998             cfg.cn.push(footer);
24999             
25000         }
25001         
25002         return  cfg;
25003     },
25004
25005     onRender : function(ct,position){
25006         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25007
25008
25009        
25010                 
25011     },
25012
25013     setHeadline: function (value)
25014     {
25015         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25016     },
25017     
25018     setFooter: function (value, href)
25019     {
25020         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25021         
25022         if(href){
25023             this.el.select('a.small-box-footer',true).first().attr('href', href);
25024         }
25025         
25026     },
25027
25028     setContent: function (value)
25029     {
25030         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25031     },
25032
25033     initEvents: function() 
25034     {   
25035         
25036     }
25037     
25038 });
25039
25040  
25041 /*
25042  * - LGPL
25043  *
25044  * TabBox
25045  * 
25046  */
25047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25048
25049 /**
25050  * @class Roo.bootstrap.dash.TabBox
25051  * @extends Roo.bootstrap.Component
25052  * Bootstrap TabBox class
25053  * @cfg {String} title Title of the TabBox
25054  * @cfg {String} icon Icon of the TabBox
25055  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25056  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25057  * 
25058  * @constructor
25059  * Create a new TabBox
25060  * @param {Object} config The config object
25061  */
25062
25063
25064 Roo.bootstrap.dash.TabBox = function(config){
25065     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25066     this.addEvents({
25067         // raw events
25068         /**
25069          * @event addpane
25070          * When a pane is added
25071          * @param {Roo.bootstrap.dash.TabPane} pane
25072          */
25073         "addpane" : true,
25074         /**
25075          * @event activatepane
25076          * When a pane is activated
25077          * @param {Roo.bootstrap.dash.TabPane} pane
25078          */
25079         "activatepane" : true
25080         
25081          
25082     });
25083     
25084     this.panes = [];
25085 };
25086
25087 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25088
25089     title : '',
25090     icon : false,
25091     showtabs : true,
25092     tabScrollable : false,
25093     
25094     getChildContainer : function()
25095     {
25096         return this.el.select('.tab-content', true).first();
25097     },
25098     
25099     getAutoCreate : function(){
25100         
25101         var header = {
25102             tag: 'li',
25103             cls: 'pull-left header',
25104             html: this.title,
25105             cn : []
25106         };
25107         
25108         if(this.icon){
25109             header.cn.push({
25110                 tag: 'i',
25111                 cls: 'fa ' + this.icon
25112             });
25113         }
25114         
25115         var h = {
25116             tag: 'ul',
25117             cls: 'nav nav-tabs pull-right',
25118             cn: [
25119                 header
25120             ]
25121         };
25122         
25123         if(this.tabScrollable){
25124             h = {
25125                 tag: 'div',
25126                 cls: 'tab-header',
25127                 cn: [
25128                     {
25129                         tag: 'ul',
25130                         cls: 'nav nav-tabs pull-right',
25131                         cn: [
25132                             header
25133                         ]
25134                     }
25135                 ]
25136             };
25137         }
25138         
25139         var cfg = {
25140             tag: 'div',
25141             cls: 'nav-tabs-custom',
25142             cn: [
25143                 h,
25144                 {
25145                     tag: 'div',
25146                     cls: 'tab-content no-padding',
25147                     cn: []
25148                 }
25149             ]
25150         };
25151
25152         return  cfg;
25153     },
25154     initEvents : function()
25155     {
25156         //Roo.log('add add pane handler');
25157         this.on('addpane', this.onAddPane, this);
25158     },
25159      /**
25160      * Updates the box title
25161      * @param {String} html to set the title to.
25162      */
25163     setTitle : function(value)
25164     {
25165         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25166     },
25167     onAddPane : function(pane)
25168     {
25169         this.panes.push(pane);
25170         //Roo.log('addpane');
25171         //Roo.log(pane);
25172         // tabs are rendere left to right..
25173         if(!this.showtabs){
25174             return;
25175         }
25176         
25177         var ctr = this.el.select('.nav-tabs', true).first();
25178          
25179          
25180         var existing = ctr.select('.nav-tab',true);
25181         var qty = existing.getCount();;
25182         
25183         
25184         var tab = ctr.createChild({
25185             tag : 'li',
25186             cls : 'nav-tab' + (qty ? '' : ' active'),
25187             cn : [
25188                 {
25189                     tag : 'a',
25190                     href:'#',
25191                     html : pane.title
25192                 }
25193             ]
25194         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25195         pane.tab = tab;
25196         
25197         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25198         if (!qty) {
25199             pane.el.addClass('active');
25200         }
25201         
25202                 
25203     },
25204     onTabClick : function(ev,un,ob,pane)
25205     {
25206         //Roo.log('tab - prev default');
25207         ev.preventDefault();
25208         
25209         
25210         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25211         pane.tab.addClass('active');
25212         //Roo.log(pane.title);
25213         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25214         // technically we should have a deactivate event.. but maybe add later.
25215         // and it should not de-activate the selected tab...
25216         this.fireEvent('activatepane', pane);
25217         pane.el.addClass('active');
25218         pane.fireEvent('activate');
25219         
25220         
25221     },
25222     
25223     getActivePane : function()
25224     {
25225         var r = false;
25226         Roo.each(this.panes, function(p) {
25227             if(p.el.hasClass('active')){
25228                 r = p;
25229                 return false;
25230             }
25231             
25232             return;
25233         });
25234         
25235         return r;
25236     }
25237     
25238     
25239 });
25240
25241  
25242 /*
25243  * - LGPL
25244  *
25245  * Tab pane
25246  * 
25247  */
25248 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25249 /**
25250  * @class Roo.bootstrap.TabPane
25251  * @extends Roo.bootstrap.Component
25252  * Bootstrap TabPane class
25253  * @cfg {Boolean} active (false | true) Default false
25254  * @cfg {String} title title of panel
25255
25256  * 
25257  * @constructor
25258  * Create a new TabPane
25259  * @param {Object} config The config object
25260  */
25261
25262 Roo.bootstrap.dash.TabPane = function(config){
25263     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25264     
25265     this.addEvents({
25266         // raw events
25267         /**
25268          * @event activate
25269          * When a pane is activated
25270          * @param {Roo.bootstrap.dash.TabPane} pane
25271          */
25272         "activate" : true
25273          
25274     });
25275 };
25276
25277 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25278     
25279     active : false,
25280     title : '',
25281     
25282     // the tabBox that this is attached to.
25283     tab : false,
25284      
25285     getAutoCreate : function() 
25286     {
25287         var cfg = {
25288             tag: 'div',
25289             cls: 'tab-pane'
25290         };
25291         
25292         if(this.active){
25293             cfg.cls += ' active';
25294         }
25295         
25296         return cfg;
25297     },
25298     initEvents  : function()
25299     {
25300         //Roo.log('trigger add pane handler');
25301         this.parent().fireEvent('addpane', this)
25302     },
25303     
25304      /**
25305      * Updates the tab title 
25306      * @param {String} html to set the title to.
25307      */
25308     setTitle: function(str)
25309     {
25310         if (!this.tab) {
25311             return;
25312         }
25313         this.title = str;
25314         this.tab.select('a', true).first().dom.innerHTML = str;
25315         
25316     }
25317     
25318     
25319     
25320 });
25321
25322  
25323
25324
25325  /*
25326  * - LGPL
25327  *
25328  * menu
25329  * 
25330  */
25331 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25332
25333 /**
25334  * @class Roo.bootstrap.menu.Menu
25335  * @extends Roo.bootstrap.Component
25336  * Bootstrap Menu class - container for Menu
25337  * @cfg {String} html Text of the menu
25338  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25339  * @cfg {String} icon Font awesome icon
25340  * @cfg {String} pos Menu align to (top | bottom) default bottom
25341  * 
25342  * 
25343  * @constructor
25344  * Create a new Menu
25345  * @param {Object} config The config object
25346  */
25347
25348
25349 Roo.bootstrap.menu.Menu = function(config){
25350     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25351     
25352     this.addEvents({
25353         /**
25354          * @event beforeshow
25355          * Fires before this menu is displayed
25356          * @param {Roo.bootstrap.menu.Menu} this
25357          */
25358         beforeshow : true,
25359         /**
25360          * @event beforehide
25361          * Fires before this menu is hidden
25362          * @param {Roo.bootstrap.menu.Menu} this
25363          */
25364         beforehide : true,
25365         /**
25366          * @event show
25367          * Fires after this menu is displayed
25368          * @param {Roo.bootstrap.menu.Menu} this
25369          */
25370         show : true,
25371         /**
25372          * @event hide
25373          * Fires after this menu is hidden
25374          * @param {Roo.bootstrap.menu.Menu} this
25375          */
25376         hide : true,
25377         /**
25378          * @event click
25379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25380          * @param {Roo.bootstrap.menu.Menu} this
25381          * @param {Roo.EventObject} e
25382          */
25383         click : true
25384     });
25385     
25386 };
25387
25388 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25389     
25390     submenu : false,
25391     html : '',
25392     weight : 'default',
25393     icon : false,
25394     pos : 'bottom',
25395     
25396     
25397     getChildContainer : function() {
25398         if(this.isSubMenu){
25399             return this.el;
25400         }
25401         
25402         return this.el.select('ul.dropdown-menu', true).first();  
25403     },
25404     
25405     getAutoCreate : function()
25406     {
25407         var text = [
25408             {
25409                 tag : 'span',
25410                 cls : 'roo-menu-text',
25411                 html : this.html
25412             }
25413         ];
25414         
25415         if(this.icon){
25416             text.unshift({
25417                 tag : 'i',
25418                 cls : 'fa ' + this.icon
25419             })
25420         }
25421         
25422         
25423         var cfg = {
25424             tag : 'div',
25425             cls : 'btn-group',
25426             cn : [
25427                 {
25428                     tag : 'button',
25429                     cls : 'dropdown-button btn btn-' + this.weight,
25430                     cn : text
25431                 },
25432                 {
25433                     tag : 'button',
25434                     cls : 'dropdown-toggle btn btn-' + this.weight,
25435                     cn : [
25436                         {
25437                             tag : 'span',
25438                             cls : 'caret'
25439                         }
25440                     ]
25441                 },
25442                 {
25443                     tag : 'ul',
25444                     cls : 'dropdown-menu'
25445                 }
25446             ]
25447             
25448         };
25449         
25450         if(this.pos == 'top'){
25451             cfg.cls += ' dropup';
25452         }
25453         
25454         if(this.isSubMenu){
25455             cfg = {
25456                 tag : 'ul',
25457                 cls : 'dropdown-menu'
25458             }
25459         }
25460         
25461         return cfg;
25462     },
25463     
25464     onRender : function(ct, position)
25465     {
25466         this.isSubMenu = ct.hasClass('dropdown-submenu');
25467         
25468         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25469     },
25470     
25471     initEvents : function() 
25472     {
25473         if(this.isSubMenu){
25474             return;
25475         }
25476         
25477         this.hidden = true;
25478         
25479         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25480         this.triggerEl.on('click', this.onTriggerPress, this);
25481         
25482         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25483         this.buttonEl.on('click', this.onClick, this);
25484         
25485     },
25486     
25487     list : function()
25488     {
25489         if(this.isSubMenu){
25490             return this.el;
25491         }
25492         
25493         return this.el.select('ul.dropdown-menu', true).first();
25494     },
25495     
25496     onClick : function(e)
25497     {
25498         this.fireEvent("click", this, e);
25499     },
25500     
25501     onTriggerPress  : function(e)
25502     {   
25503         if (this.isVisible()) {
25504             this.hide();
25505         } else {
25506             this.show();
25507         }
25508     },
25509     
25510     isVisible : function(){
25511         return !this.hidden;
25512     },
25513     
25514     show : function()
25515     {
25516         this.fireEvent("beforeshow", this);
25517         
25518         this.hidden = false;
25519         this.el.addClass('open');
25520         
25521         Roo.get(document).on("mouseup", this.onMouseUp, this);
25522         
25523         this.fireEvent("show", this);
25524         
25525         
25526     },
25527     
25528     hide : function()
25529     {
25530         this.fireEvent("beforehide", this);
25531         
25532         this.hidden = true;
25533         this.el.removeClass('open');
25534         
25535         Roo.get(document).un("mouseup", this.onMouseUp);
25536         
25537         this.fireEvent("hide", this);
25538     },
25539     
25540     onMouseUp : function()
25541     {
25542         this.hide();
25543     }
25544     
25545 });
25546
25547  
25548  /*
25549  * - LGPL
25550  *
25551  * menu item
25552  * 
25553  */
25554 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25555
25556 /**
25557  * @class Roo.bootstrap.menu.Item
25558  * @extends Roo.bootstrap.Component
25559  * Bootstrap MenuItem class
25560  * @cfg {Boolean} submenu (true | false) default false
25561  * @cfg {String} html text of the item
25562  * @cfg {String} href the link
25563  * @cfg {Boolean} disable (true | false) default false
25564  * @cfg {Boolean} preventDefault (true | false) default true
25565  * @cfg {String} icon Font awesome icon
25566  * @cfg {String} pos Submenu align to (left | right) default right 
25567  * 
25568  * 
25569  * @constructor
25570  * Create a new Item
25571  * @param {Object} config The config object
25572  */
25573
25574
25575 Roo.bootstrap.menu.Item = function(config){
25576     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25577     this.addEvents({
25578         /**
25579          * @event mouseover
25580          * Fires when the mouse is hovering over this menu
25581          * @param {Roo.bootstrap.menu.Item} this
25582          * @param {Roo.EventObject} e
25583          */
25584         mouseover : true,
25585         /**
25586          * @event mouseout
25587          * Fires when the mouse exits this menu
25588          * @param {Roo.bootstrap.menu.Item} this
25589          * @param {Roo.EventObject} e
25590          */
25591         mouseout : true,
25592         // raw events
25593         /**
25594          * @event click
25595          * The raw click event for the entire grid.
25596          * @param {Roo.EventObject} e
25597          */
25598         click : true
25599     });
25600 };
25601
25602 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25603     
25604     submenu : false,
25605     href : '',
25606     html : '',
25607     preventDefault: true,
25608     disable : false,
25609     icon : false,
25610     pos : 'right',
25611     
25612     getAutoCreate : function()
25613     {
25614         var text = [
25615             {
25616                 tag : 'span',
25617                 cls : 'roo-menu-item-text',
25618                 html : this.html
25619             }
25620         ];
25621         
25622         if(this.icon){
25623             text.unshift({
25624                 tag : 'i',
25625                 cls : 'fa ' + this.icon
25626             })
25627         }
25628         
25629         var cfg = {
25630             tag : 'li',
25631             cn : [
25632                 {
25633                     tag : 'a',
25634                     href : this.href || '#',
25635                     cn : text
25636                 }
25637             ]
25638         };
25639         
25640         if(this.disable){
25641             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25642         }
25643         
25644         if(this.submenu){
25645             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25646             
25647             if(this.pos == 'left'){
25648                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25649             }
25650         }
25651         
25652         return cfg;
25653     },
25654     
25655     initEvents : function() 
25656     {
25657         this.el.on('mouseover', this.onMouseOver, this);
25658         this.el.on('mouseout', this.onMouseOut, this);
25659         
25660         this.el.select('a', true).first().on('click', this.onClick, this);
25661         
25662     },
25663     
25664     onClick : function(e)
25665     {
25666         if(this.preventDefault){
25667             e.preventDefault();
25668         }
25669         
25670         this.fireEvent("click", this, e);
25671     },
25672     
25673     onMouseOver : function(e)
25674     {
25675         if(this.submenu && this.pos == 'left'){
25676             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25677         }
25678         
25679         this.fireEvent("mouseover", this, e);
25680     },
25681     
25682     onMouseOut : function(e)
25683     {
25684         this.fireEvent("mouseout", this, e);
25685     }
25686 });
25687
25688  
25689
25690  /*
25691  * - LGPL
25692  *
25693  * menu separator
25694  * 
25695  */
25696 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25697
25698 /**
25699  * @class Roo.bootstrap.menu.Separator
25700  * @extends Roo.bootstrap.Component
25701  * Bootstrap Separator class
25702  * 
25703  * @constructor
25704  * Create a new Separator
25705  * @param {Object} config The config object
25706  */
25707
25708
25709 Roo.bootstrap.menu.Separator = function(config){
25710     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25711 };
25712
25713 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25714     
25715     getAutoCreate : function(){
25716         var cfg = {
25717             tag : 'li',
25718             cls: 'divider'
25719         };
25720         
25721         return cfg;
25722     }
25723    
25724 });
25725
25726  
25727
25728  /*
25729  * - LGPL
25730  *
25731  * Tooltip
25732  * 
25733  */
25734
25735 /**
25736  * @class Roo.bootstrap.Tooltip
25737  * Bootstrap Tooltip class
25738  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25739  * to determine which dom element triggers the tooltip.
25740  * 
25741  * It needs to add support for additional attributes like tooltip-position
25742  * 
25743  * @constructor
25744  * Create a new Toolti
25745  * @param {Object} config The config object
25746  */
25747
25748 Roo.bootstrap.Tooltip = function(config){
25749     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25750     
25751     this.alignment = Roo.bootstrap.Tooltip.alignment;
25752     
25753     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25754         this.alignment = config.alignment;
25755     }
25756     
25757 };
25758
25759 Roo.apply(Roo.bootstrap.Tooltip, {
25760     /**
25761      * @function init initialize tooltip monitoring.
25762      * @static
25763      */
25764     currentEl : false,
25765     currentTip : false,
25766     currentRegion : false,
25767     
25768     //  init : delay?
25769     
25770     init : function()
25771     {
25772         Roo.get(document).on('mouseover', this.enter ,this);
25773         Roo.get(document).on('mouseout', this.leave, this);
25774          
25775         
25776         this.currentTip = new Roo.bootstrap.Tooltip();
25777     },
25778     
25779     enter : function(ev)
25780     {
25781         var dom = ev.getTarget();
25782         
25783         //Roo.log(['enter',dom]);
25784         var el = Roo.fly(dom);
25785         if (this.currentEl) {
25786             //Roo.log(dom);
25787             //Roo.log(this.currentEl);
25788             //Roo.log(this.currentEl.contains(dom));
25789             if (this.currentEl == el) {
25790                 return;
25791             }
25792             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25793                 return;
25794             }
25795
25796         }
25797         
25798         if (this.currentTip.el) {
25799             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25800         }    
25801         //Roo.log(ev);
25802         
25803         if(!el || el.dom == document){
25804             return;
25805         }
25806         
25807         var bindEl = el;
25808         
25809         // you can not look for children, as if el is the body.. then everythign is the child..
25810         if (!el.attr('tooltip')) { //
25811             if (!el.select("[tooltip]").elements.length) {
25812                 return;
25813             }
25814             // is the mouse over this child...?
25815             bindEl = el.select("[tooltip]").first();
25816             var xy = ev.getXY();
25817             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25818                 //Roo.log("not in region.");
25819                 return;
25820             }
25821             //Roo.log("child element over..");
25822             
25823         }
25824         this.currentEl = bindEl;
25825         this.currentTip.bind(bindEl);
25826         this.currentRegion = Roo.lib.Region.getRegion(dom);
25827         this.currentTip.enter();
25828         
25829     },
25830     leave : function(ev)
25831     {
25832         var dom = ev.getTarget();
25833         //Roo.log(['leave',dom]);
25834         if (!this.currentEl) {
25835             return;
25836         }
25837         
25838         
25839         if (dom != this.currentEl.dom) {
25840             return;
25841         }
25842         var xy = ev.getXY();
25843         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25844             return;
25845         }
25846         // only activate leave if mouse cursor is outside... bounding box..
25847         
25848         
25849         
25850         
25851         if (this.currentTip) {
25852             this.currentTip.leave();
25853         }
25854         //Roo.log('clear currentEl');
25855         this.currentEl = false;
25856         
25857         
25858     },
25859     alignment : {
25860         'left' : ['r-l', [-2,0], 'right'],
25861         'right' : ['l-r', [2,0], 'left'],
25862         'bottom' : ['t-b', [0,2], 'top'],
25863         'top' : [ 'b-t', [0,-2], 'bottom']
25864     }
25865     
25866 });
25867
25868
25869 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25870     
25871     
25872     bindEl : false,
25873     
25874     delay : null, // can be { show : 300 , hide: 500}
25875     
25876     timeout : null,
25877     
25878     hoverState : null, //???
25879     
25880     placement : 'bottom', 
25881     
25882     alignment : false,
25883     
25884     getAutoCreate : function(){
25885     
25886         var cfg = {
25887            cls : 'tooltip',
25888            role : 'tooltip',
25889            cn : [
25890                 {
25891                     cls : 'tooltip-arrow'
25892                 },
25893                 {
25894                     cls : 'tooltip-inner'
25895                 }
25896            ]
25897         };
25898         
25899         return cfg;
25900     },
25901     bind : function(el)
25902     {
25903         this.bindEl = el;
25904     },
25905       
25906     
25907     enter : function () {
25908        
25909         if (this.timeout != null) {
25910             clearTimeout(this.timeout);
25911         }
25912         
25913         this.hoverState = 'in';
25914          //Roo.log("enter - show");
25915         if (!this.delay || !this.delay.show) {
25916             this.show();
25917             return;
25918         }
25919         var _t = this;
25920         this.timeout = setTimeout(function () {
25921             if (_t.hoverState == 'in') {
25922                 _t.show();
25923             }
25924         }, this.delay.show);
25925     },
25926     leave : function()
25927     {
25928         clearTimeout(this.timeout);
25929     
25930         this.hoverState = 'out';
25931          if (!this.delay || !this.delay.hide) {
25932             this.hide();
25933             return;
25934         }
25935        
25936         var _t = this;
25937         this.timeout = setTimeout(function () {
25938             //Roo.log("leave - timeout");
25939             
25940             if (_t.hoverState == 'out') {
25941                 _t.hide();
25942                 Roo.bootstrap.Tooltip.currentEl = false;
25943             }
25944         }, delay);
25945     },
25946     
25947     show : function (msg)
25948     {
25949         if (!this.el) {
25950             this.render(document.body);
25951         }
25952         // set content.
25953         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25954         
25955         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25956         
25957         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25958         
25959         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25960         
25961         var placement = typeof this.placement == 'function' ?
25962             this.placement.call(this, this.el, on_el) :
25963             this.placement;
25964             
25965         var autoToken = /\s?auto?\s?/i;
25966         var autoPlace = autoToken.test(placement);
25967         if (autoPlace) {
25968             placement = placement.replace(autoToken, '') || 'top';
25969         }
25970         
25971         //this.el.detach()
25972         //this.el.setXY([0,0]);
25973         this.el.show();
25974         //this.el.dom.style.display='block';
25975         
25976         //this.el.appendTo(on_el);
25977         
25978         var p = this.getPosition();
25979         var box = this.el.getBox();
25980         
25981         if (autoPlace) {
25982             // fixme..
25983         }
25984         
25985         var align = this.alignment[placement];
25986         
25987         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25988         
25989         if(placement == 'top' || placement == 'bottom'){
25990             if(xy[0] < 0){
25991                 placement = 'right';
25992             }
25993             
25994             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25995                 placement = 'left';
25996             }
25997             
25998             var scroll = Roo.select('body', true).first().getScroll();
25999             
26000             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26001                 placement = 'top';
26002             }
26003             
26004         }
26005         
26006         this.el.alignTo(this.bindEl, align[0],align[1]);
26007         //var arrow = this.el.select('.arrow',true).first();
26008         //arrow.set(align[2], 
26009         
26010         this.el.addClass(placement);
26011         
26012         this.el.addClass('in fade');
26013         
26014         this.hoverState = null;
26015         
26016         if (this.el.hasClass('fade')) {
26017             // fade it?
26018         }
26019         
26020     },
26021     hide : function()
26022     {
26023          
26024         if (!this.el) {
26025             return;
26026         }
26027         //this.el.setXY([0,0]);
26028         this.el.removeClass('in');
26029         //this.el.hide();
26030         
26031     }
26032     
26033 });
26034  
26035
26036  /*
26037  * - LGPL
26038  *
26039  * Location Picker
26040  * 
26041  */
26042
26043 /**
26044  * @class Roo.bootstrap.LocationPicker
26045  * @extends Roo.bootstrap.Component
26046  * Bootstrap LocationPicker class
26047  * @cfg {Number} latitude Position when init default 0
26048  * @cfg {Number} longitude Position when init default 0
26049  * @cfg {Number} zoom default 15
26050  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26051  * @cfg {Boolean} mapTypeControl default false
26052  * @cfg {Boolean} disableDoubleClickZoom default false
26053  * @cfg {Boolean} scrollwheel default true
26054  * @cfg {Boolean} streetViewControl default false
26055  * @cfg {Number} radius default 0
26056  * @cfg {String} locationName
26057  * @cfg {Boolean} draggable default true
26058  * @cfg {Boolean} enableAutocomplete default false
26059  * @cfg {Boolean} enableReverseGeocode default true
26060  * @cfg {String} markerTitle
26061  * 
26062  * @constructor
26063  * Create a new LocationPicker
26064  * @param {Object} config The config object
26065  */
26066
26067
26068 Roo.bootstrap.LocationPicker = function(config){
26069     
26070     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26071     
26072     this.addEvents({
26073         /**
26074          * @event initial
26075          * Fires when the picker initialized.
26076          * @param {Roo.bootstrap.LocationPicker} this
26077          * @param {Google Location} location
26078          */
26079         initial : true,
26080         /**
26081          * @event positionchanged
26082          * Fires when the picker position changed.
26083          * @param {Roo.bootstrap.LocationPicker} this
26084          * @param {Google Location} location
26085          */
26086         positionchanged : true,
26087         /**
26088          * @event resize
26089          * Fires when the map resize.
26090          * @param {Roo.bootstrap.LocationPicker} this
26091          */
26092         resize : true,
26093         /**
26094          * @event show
26095          * Fires when the map show.
26096          * @param {Roo.bootstrap.LocationPicker} this
26097          */
26098         show : true,
26099         /**
26100          * @event hide
26101          * Fires when the map hide.
26102          * @param {Roo.bootstrap.LocationPicker} this
26103          */
26104         hide : true,
26105         /**
26106          * @event mapClick
26107          * Fires when click the map.
26108          * @param {Roo.bootstrap.LocationPicker} this
26109          * @param {Map event} e
26110          */
26111         mapClick : true,
26112         /**
26113          * @event mapRightClick
26114          * Fires when right click the map.
26115          * @param {Roo.bootstrap.LocationPicker} this
26116          * @param {Map event} e
26117          */
26118         mapRightClick : true,
26119         /**
26120          * @event markerClick
26121          * Fires when click the marker.
26122          * @param {Roo.bootstrap.LocationPicker} this
26123          * @param {Map event} e
26124          */
26125         markerClick : true,
26126         /**
26127          * @event markerRightClick
26128          * Fires when right click the marker.
26129          * @param {Roo.bootstrap.LocationPicker} this
26130          * @param {Map event} e
26131          */
26132         markerRightClick : true,
26133         /**
26134          * @event OverlayViewDraw
26135          * Fires when OverlayView Draw
26136          * @param {Roo.bootstrap.LocationPicker} this
26137          */
26138         OverlayViewDraw : true,
26139         /**
26140          * @event OverlayViewOnAdd
26141          * Fires when OverlayView Draw
26142          * @param {Roo.bootstrap.LocationPicker} this
26143          */
26144         OverlayViewOnAdd : true,
26145         /**
26146          * @event OverlayViewOnRemove
26147          * Fires when OverlayView Draw
26148          * @param {Roo.bootstrap.LocationPicker} this
26149          */
26150         OverlayViewOnRemove : true,
26151         /**
26152          * @event OverlayViewShow
26153          * Fires when OverlayView Draw
26154          * @param {Roo.bootstrap.LocationPicker} this
26155          * @param {Pixel} cpx
26156          */
26157         OverlayViewShow : true,
26158         /**
26159          * @event OverlayViewHide
26160          * Fires when OverlayView Draw
26161          * @param {Roo.bootstrap.LocationPicker} this
26162          */
26163         OverlayViewHide : true,
26164         /**
26165          * @event loadexception
26166          * Fires when load google lib failed.
26167          * @param {Roo.bootstrap.LocationPicker} this
26168          */
26169         loadexception : true
26170     });
26171         
26172 };
26173
26174 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26175     
26176     gMapContext: false,
26177     
26178     latitude: 0,
26179     longitude: 0,
26180     zoom: 15,
26181     mapTypeId: false,
26182     mapTypeControl: false,
26183     disableDoubleClickZoom: false,
26184     scrollwheel: true,
26185     streetViewControl: false,
26186     radius: 0,
26187     locationName: '',
26188     draggable: true,
26189     enableAutocomplete: false,
26190     enableReverseGeocode: true,
26191     markerTitle: '',
26192     
26193     getAutoCreate: function()
26194     {
26195
26196         var cfg = {
26197             tag: 'div',
26198             cls: 'roo-location-picker'
26199         };
26200         
26201         return cfg
26202     },
26203     
26204     initEvents: function(ct, position)
26205     {       
26206         if(!this.el.getWidth() || this.isApplied()){
26207             return;
26208         }
26209         
26210         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26211         
26212         this.initial();
26213     },
26214     
26215     initial: function()
26216     {
26217         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26218             this.fireEvent('loadexception', this);
26219             return;
26220         }
26221         
26222         if(!this.mapTypeId){
26223             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26224         }
26225         
26226         this.gMapContext = this.GMapContext();
26227         
26228         this.initOverlayView();
26229         
26230         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26231         
26232         var _this = this;
26233                 
26234         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26235             _this.setPosition(_this.gMapContext.marker.position);
26236         });
26237         
26238         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26239             _this.fireEvent('mapClick', this, event);
26240             
26241         });
26242
26243         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26244             _this.fireEvent('mapRightClick', this, event);
26245             
26246         });
26247         
26248         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26249             _this.fireEvent('markerClick', this, event);
26250             
26251         });
26252
26253         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26254             _this.fireEvent('markerRightClick', this, event);
26255             
26256         });
26257         
26258         this.setPosition(this.gMapContext.location);
26259         
26260         this.fireEvent('initial', this, this.gMapContext.location);
26261     },
26262     
26263     initOverlayView: function()
26264     {
26265         var _this = this;
26266         
26267         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26268             
26269             draw: function()
26270             {
26271                 _this.fireEvent('OverlayViewDraw', _this);
26272             },
26273             
26274             onAdd: function()
26275             {
26276                 _this.fireEvent('OverlayViewOnAdd', _this);
26277             },
26278             
26279             onRemove: function()
26280             {
26281                 _this.fireEvent('OverlayViewOnRemove', _this);
26282             },
26283             
26284             show: function(cpx)
26285             {
26286                 _this.fireEvent('OverlayViewShow', _this, cpx);
26287             },
26288             
26289             hide: function()
26290             {
26291                 _this.fireEvent('OverlayViewHide', _this);
26292             }
26293             
26294         });
26295     },
26296     
26297     fromLatLngToContainerPixel: function(event)
26298     {
26299         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26300     },
26301     
26302     isApplied: function() 
26303     {
26304         return this.getGmapContext() == false ? false : true;
26305     },
26306     
26307     getGmapContext: function() 
26308     {
26309         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26310     },
26311     
26312     GMapContext: function() 
26313     {
26314         var position = new google.maps.LatLng(this.latitude, this.longitude);
26315         
26316         var _map = new google.maps.Map(this.el.dom, {
26317             center: position,
26318             zoom: this.zoom,
26319             mapTypeId: this.mapTypeId,
26320             mapTypeControl: this.mapTypeControl,
26321             disableDoubleClickZoom: this.disableDoubleClickZoom,
26322             scrollwheel: this.scrollwheel,
26323             streetViewControl: this.streetViewControl,
26324             locationName: this.locationName,
26325             draggable: this.draggable,
26326             enableAutocomplete: this.enableAutocomplete,
26327             enableReverseGeocode: this.enableReverseGeocode
26328         });
26329         
26330         var _marker = new google.maps.Marker({
26331             position: position,
26332             map: _map,
26333             title: this.markerTitle,
26334             draggable: this.draggable
26335         });
26336         
26337         return {
26338             map: _map,
26339             marker: _marker,
26340             circle: null,
26341             location: position,
26342             radius: this.radius,
26343             locationName: this.locationName,
26344             addressComponents: {
26345                 formatted_address: null,
26346                 addressLine1: null,
26347                 addressLine2: null,
26348                 streetName: null,
26349                 streetNumber: null,
26350                 city: null,
26351                 district: null,
26352                 state: null,
26353                 stateOrProvince: null
26354             },
26355             settings: this,
26356             domContainer: this.el.dom,
26357             geodecoder: new google.maps.Geocoder()
26358         };
26359     },
26360     
26361     drawCircle: function(center, radius, options) 
26362     {
26363         if (this.gMapContext.circle != null) {
26364             this.gMapContext.circle.setMap(null);
26365         }
26366         if (radius > 0) {
26367             radius *= 1;
26368             options = Roo.apply({}, options, {
26369                 strokeColor: "#0000FF",
26370                 strokeOpacity: .35,
26371                 strokeWeight: 2,
26372                 fillColor: "#0000FF",
26373                 fillOpacity: .2
26374             });
26375             
26376             options.map = this.gMapContext.map;
26377             options.radius = radius;
26378             options.center = center;
26379             this.gMapContext.circle = new google.maps.Circle(options);
26380             return this.gMapContext.circle;
26381         }
26382         
26383         return null;
26384     },
26385     
26386     setPosition: function(location) 
26387     {
26388         this.gMapContext.location = location;
26389         this.gMapContext.marker.setPosition(location);
26390         this.gMapContext.map.panTo(location);
26391         this.drawCircle(location, this.gMapContext.radius, {});
26392         
26393         var _this = this;
26394         
26395         if (this.gMapContext.settings.enableReverseGeocode) {
26396             this.gMapContext.geodecoder.geocode({
26397                 latLng: this.gMapContext.location
26398             }, function(results, status) {
26399                 
26400                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26401                     _this.gMapContext.locationName = results[0].formatted_address;
26402                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26403                     
26404                     _this.fireEvent('positionchanged', this, location);
26405                 }
26406             });
26407             
26408             return;
26409         }
26410         
26411         this.fireEvent('positionchanged', this, location);
26412     },
26413     
26414     resize: function()
26415     {
26416         google.maps.event.trigger(this.gMapContext.map, "resize");
26417         
26418         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26419         
26420         this.fireEvent('resize', this);
26421     },
26422     
26423     setPositionByLatLng: function(latitude, longitude)
26424     {
26425         this.setPosition(new google.maps.LatLng(latitude, longitude));
26426     },
26427     
26428     getCurrentPosition: function() 
26429     {
26430         return {
26431             latitude: this.gMapContext.location.lat(),
26432             longitude: this.gMapContext.location.lng()
26433         };
26434     },
26435     
26436     getAddressName: function() 
26437     {
26438         return this.gMapContext.locationName;
26439     },
26440     
26441     getAddressComponents: function() 
26442     {
26443         return this.gMapContext.addressComponents;
26444     },
26445     
26446     address_component_from_google_geocode: function(address_components) 
26447     {
26448         var result = {};
26449         
26450         for (var i = 0; i < address_components.length; i++) {
26451             var component = address_components[i];
26452             if (component.types.indexOf("postal_code") >= 0) {
26453                 result.postalCode = component.short_name;
26454             } else if (component.types.indexOf("street_number") >= 0) {
26455                 result.streetNumber = component.short_name;
26456             } else if (component.types.indexOf("route") >= 0) {
26457                 result.streetName = component.short_name;
26458             } else if (component.types.indexOf("neighborhood") >= 0) {
26459                 result.city = component.short_name;
26460             } else if (component.types.indexOf("locality") >= 0) {
26461                 result.city = component.short_name;
26462             } else if (component.types.indexOf("sublocality") >= 0) {
26463                 result.district = component.short_name;
26464             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26465                 result.stateOrProvince = component.short_name;
26466             } else if (component.types.indexOf("country") >= 0) {
26467                 result.country = component.short_name;
26468             }
26469         }
26470         
26471         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26472         result.addressLine2 = "";
26473         return result;
26474     },
26475     
26476     setZoomLevel: function(zoom)
26477     {
26478         this.gMapContext.map.setZoom(zoom);
26479     },
26480     
26481     show: function()
26482     {
26483         if(!this.el){
26484             return;
26485         }
26486         
26487         this.el.show();
26488         
26489         this.resize();
26490         
26491         this.fireEvent('show', this);
26492     },
26493     
26494     hide: function()
26495     {
26496         if(!this.el){
26497             return;
26498         }
26499         
26500         this.el.hide();
26501         
26502         this.fireEvent('hide', this);
26503     }
26504     
26505 });
26506
26507 Roo.apply(Roo.bootstrap.LocationPicker, {
26508     
26509     OverlayView : function(map, options)
26510     {
26511         options = options || {};
26512         
26513         this.setMap(map);
26514     }
26515     
26516     
26517 });/*
26518  * - LGPL
26519  *
26520  * Alert
26521  * 
26522  */
26523
26524 /**
26525  * @class Roo.bootstrap.Alert
26526  * @extends Roo.bootstrap.Component
26527  * Bootstrap Alert class
26528  * @cfg {String} title The title of alert
26529  * @cfg {String} html The content of alert
26530  * @cfg {String} weight (  success | info | warning | danger )
26531  * @cfg {String} faicon font-awesomeicon
26532  * 
26533  * @constructor
26534  * Create a new alert
26535  * @param {Object} config The config object
26536  */
26537
26538
26539 Roo.bootstrap.Alert = function(config){
26540     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26541     
26542 };
26543
26544 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26545     
26546     title: '',
26547     html: '',
26548     weight: false,
26549     faicon: false,
26550     
26551     getAutoCreate : function()
26552     {
26553         
26554         var cfg = {
26555             tag : 'div',
26556             cls : 'alert',
26557             cn : [
26558                 {
26559                     tag : 'i',
26560                     cls : 'roo-alert-icon'
26561                     
26562                 },
26563                 {
26564                     tag : 'b',
26565                     cls : 'roo-alert-title',
26566                     html : this.title
26567                 },
26568                 {
26569                     tag : 'span',
26570                     cls : 'roo-alert-text',
26571                     html : this.html
26572                 }
26573             ]
26574         };
26575         
26576         if(this.faicon){
26577             cfg.cn[0].cls += ' fa ' + this.faicon;
26578         }
26579         
26580         if(this.weight){
26581             cfg.cls += ' alert-' + this.weight;
26582         }
26583         
26584         return cfg;
26585     },
26586     
26587     initEvents: function() 
26588     {
26589         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26590     },
26591     
26592     setTitle : function(str)
26593     {
26594         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26595     },
26596     
26597     setText : function(str)
26598     {
26599         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26600     },
26601     
26602     setWeight : function(weight)
26603     {
26604         if(this.weight){
26605             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26606         }
26607         
26608         this.weight = weight;
26609         
26610         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26611     },
26612     
26613     setIcon : function(icon)
26614     {
26615         if(this.faicon){
26616             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26617         }
26618         
26619         this.faicon = icon;
26620         
26621         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26622     },
26623     
26624     hide: function() 
26625     {
26626         this.el.hide();   
26627     },
26628     
26629     show: function() 
26630     {  
26631         this.el.show();   
26632     }
26633     
26634 });
26635
26636  
26637 /*
26638 * Licence: LGPL
26639 */
26640
26641 /**
26642  * @class Roo.bootstrap.UploadCropbox
26643  * @extends Roo.bootstrap.Component
26644  * Bootstrap UploadCropbox class
26645  * @cfg {String} emptyText show when image has been loaded
26646  * @cfg {String} rotateNotify show when image too small to rotate
26647  * @cfg {Number} errorTimeout default 3000
26648  * @cfg {Number} minWidth default 300
26649  * @cfg {Number} minHeight default 300
26650  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26651  * @cfg {Boolean} isDocument (true|false) default false
26652  * @cfg {String} url action url
26653  * @cfg {String} paramName default 'imageUpload'
26654  * @cfg {String} method default POST
26655  * @cfg {Boolean} loadMask (true|false) default true
26656  * @cfg {Boolean} loadingText default 'Loading...'
26657  * 
26658  * @constructor
26659  * Create a new UploadCropbox
26660  * @param {Object} config The config object
26661  */
26662
26663 Roo.bootstrap.UploadCropbox = function(config){
26664     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26665     
26666     this.addEvents({
26667         /**
26668          * @event beforeselectfile
26669          * Fire before select file
26670          * @param {Roo.bootstrap.UploadCropbox} this
26671          */
26672         "beforeselectfile" : true,
26673         /**
26674          * @event initial
26675          * Fire after initEvent
26676          * @param {Roo.bootstrap.UploadCropbox} this
26677          */
26678         "initial" : true,
26679         /**
26680          * @event crop
26681          * Fire after initEvent
26682          * @param {Roo.bootstrap.UploadCropbox} this
26683          * @param {String} data
26684          */
26685         "crop" : true,
26686         /**
26687          * @event prepare
26688          * Fire when preparing the file data
26689          * @param {Roo.bootstrap.UploadCropbox} this
26690          * @param {Object} file
26691          */
26692         "prepare" : true,
26693         /**
26694          * @event exception
26695          * Fire when get exception
26696          * @param {Roo.bootstrap.UploadCropbox} this
26697          * @param {XMLHttpRequest} xhr
26698          */
26699         "exception" : true,
26700         /**
26701          * @event beforeloadcanvas
26702          * Fire before load the canvas
26703          * @param {Roo.bootstrap.UploadCropbox} this
26704          * @param {String} src
26705          */
26706         "beforeloadcanvas" : true,
26707         /**
26708          * @event trash
26709          * Fire when trash image
26710          * @param {Roo.bootstrap.UploadCropbox} this
26711          */
26712         "trash" : true,
26713         /**
26714          * @event download
26715          * Fire when download the image
26716          * @param {Roo.bootstrap.UploadCropbox} this
26717          */
26718         "download" : true,
26719         /**
26720          * @event footerbuttonclick
26721          * Fire when footerbuttonclick
26722          * @param {Roo.bootstrap.UploadCropbox} this
26723          * @param {String} type
26724          */
26725         "footerbuttonclick" : true,
26726         /**
26727          * @event resize
26728          * Fire when resize
26729          * @param {Roo.bootstrap.UploadCropbox} this
26730          */
26731         "resize" : true,
26732         /**
26733          * @event rotate
26734          * Fire when rotate the image
26735          * @param {Roo.bootstrap.UploadCropbox} this
26736          * @param {String} pos
26737          */
26738         "rotate" : true,
26739         /**
26740          * @event inspect
26741          * Fire when inspect the file
26742          * @param {Roo.bootstrap.UploadCropbox} this
26743          * @param {Object} file
26744          */
26745         "inspect" : true,
26746         /**
26747          * @event upload
26748          * Fire when xhr upload the file
26749          * @param {Roo.bootstrap.UploadCropbox} this
26750          * @param {Object} data
26751          */
26752         "upload" : true,
26753         /**
26754          * @event arrange
26755          * Fire when arrange the file data
26756          * @param {Roo.bootstrap.UploadCropbox} this
26757          * @param {Object} formData
26758          */
26759         "arrange" : true
26760     });
26761     
26762     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26763 };
26764
26765 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26766     
26767     emptyText : 'Click to upload image',
26768     rotateNotify : 'Image is too small to rotate',
26769     errorTimeout : 3000,
26770     scale : 0,
26771     baseScale : 1,
26772     rotate : 0,
26773     dragable : false,
26774     pinching : false,
26775     mouseX : 0,
26776     mouseY : 0,
26777     cropData : false,
26778     minWidth : 300,
26779     minHeight : 300,
26780     file : false,
26781     exif : {},
26782     baseRotate : 1,
26783     cropType : 'image/jpeg',
26784     buttons : false,
26785     canvasLoaded : false,
26786     isDocument : false,
26787     method : 'POST',
26788     paramName : 'imageUpload',
26789     loadMask : true,
26790     loadingText : 'Loading...',
26791     maskEl : false,
26792     
26793     getAutoCreate : function()
26794     {
26795         var cfg = {
26796             tag : 'div',
26797             cls : 'roo-upload-cropbox',
26798             cn : [
26799                 {
26800                     tag : 'input',
26801                     cls : 'roo-upload-cropbox-selector',
26802                     type : 'file'
26803                 },
26804                 {
26805                     tag : 'div',
26806                     cls : 'roo-upload-cropbox-body',
26807                     style : 'cursor:pointer',
26808                     cn : [
26809                         {
26810                             tag : 'div',
26811                             cls : 'roo-upload-cropbox-preview'
26812                         },
26813                         {
26814                             tag : 'div',
26815                             cls : 'roo-upload-cropbox-thumb'
26816                         },
26817                         {
26818                             tag : 'div',
26819                             cls : 'roo-upload-cropbox-empty-notify',
26820                             html : this.emptyText
26821                         },
26822                         {
26823                             tag : 'div',
26824                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26825                             html : this.rotateNotify
26826                         }
26827                     ]
26828                 },
26829                 {
26830                     tag : 'div',
26831                     cls : 'roo-upload-cropbox-footer',
26832                     cn : {
26833                         tag : 'div',
26834                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26835                         cn : []
26836                     }
26837                 }
26838             ]
26839         };
26840         
26841         return cfg;
26842     },
26843     
26844     onRender : function(ct, position)
26845     {
26846         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26847         
26848         if (this.buttons.length) {
26849             
26850             Roo.each(this.buttons, function(bb) {
26851                 
26852                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26853                 
26854                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26855                 
26856             }, this);
26857         }
26858         
26859         if(this.loadMask){
26860             this.maskEl = this.el;
26861         }
26862     },
26863     
26864     initEvents : function()
26865     {
26866         this.urlAPI = (window.createObjectURL && window) || 
26867                                 (window.URL && URL.revokeObjectURL && URL) || 
26868                                 (window.webkitURL && webkitURL);
26869                         
26870         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26871         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26872         
26873         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26874         this.selectorEl.hide();
26875         
26876         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26877         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26878         
26879         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26880         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26881         this.thumbEl.hide();
26882         
26883         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26884         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26885         
26886         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26887         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26888         this.errorEl.hide();
26889         
26890         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26891         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26892         this.footerEl.hide();
26893         
26894         this.setThumbBoxSize();
26895         
26896         this.bind();
26897         
26898         this.resize();
26899         
26900         this.fireEvent('initial', this);
26901     },
26902
26903     bind : function()
26904     {
26905         var _this = this;
26906         
26907         window.addEventListener("resize", function() { _this.resize(); } );
26908         
26909         this.bodyEl.on('click', this.beforeSelectFile, this);
26910         
26911         if(Roo.isTouch){
26912             this.bodyEl.on('touchstart', this.onTouchStart, this);
26913             this.bodyEl.on('touchmove', this.onTouchMove, this);
26914             this.bodyEl.on('touchend', this.onTouchEnd, this);
26915         }
26916         
26917         if(!Roo.isTouch){
26918             this.bodyEl.on('mousedown', this.onMouseDown, this);
26919             this.bodyEl.on('mousemove', this.onMouseMove, this);
26920             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26921             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26922             Roo.get(document).on('mouseup', this.onMouseUp, this);
26923         }
26924         
26925         this.selectorEl.on('change', this.onFileSelected, this);
26926     },
26927     
26928     reset : function()
26929     {    
26930         this.scale = 0;
26931         this.baseScale = 1;
26932         this.rotate = 0;
26933         this.baseRotate = 1;
26934         this.dragable = false;
26935         this.pinching = false;
26936         this.mouseX = 0;
26937         this.mouseY = 0;
26938         this.cropData = false;
26939         this.notifyEl.dom.innerHTML = this.emptyText;
26940         
26941         this.selectorEl.dom.value = '';
26942         
26943     },
26944     
26945     resize : function()
26946     {
26947         if(this.fireEvent('resize', this) != false){
26948             this.setThumbBoxPosition();
26949             this.setCanvasPosition();
26950         }
26951     },
26952     
26953     onFooterButtonClick : function(e, el, o, type)
26954     {
26955         switch (type) {
26956             case 'rotate-left' :
26957                 this.onRotateLeft(e);
26958                 break;
26959             case 'rotate-right' :
26960                 this.onRotateRight(e);
26961                 break;
26962             case 'picture' :
26963                 this.beforeSelectFile(e);
26964                 break;
26965             case 'trash' :
26966                 this.trash(e);
26967                 break;
26968             case 'crop' :
26969                 this.crop(e);
26970                 break;
26971             case 'download' :
26972                 this.download(e);
26973                 break;
26974             default :
26975                 break;
26976         }
26977         
26978         this.fireEvent('footerbuttonclick', this, type);
26979     },
26980     
26981     beforeSelectFile : function(e)
26982     {
26983         e.preventDefault();
26984         
26985         if(this.fireEvent('beforeselectfile', this) != false){
26986             this.selectorEl.dom.click();
26987         }
26988     },
26989     
26990     onFileSelected : function(e)
26991     {
26992         e.preventDefault();
26993         
26994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26995             return;
26996         }
26997         
26998         var file = this.selectorEl.dom.files[0];
26999         
27000         if(this.fireEvent('inspect', this, file) != false){
27001             this.prepare(file);
27002         }
27003         
27004     },
27005     
27006     trash : function(e)
27007     {
27008         this.fireEvent('trash', this);
27009     },
27010     
27011     download : function(e)
27012     {
27013         this.fireEvent('download', this);
27014     },
27015     
27016     loadCanvas : function(src)
27017     {   
27018         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27019             
27020             this.reset();
27021             
27022             this.imageEl = document.createElement('img');
27023             
27024             var _this = this;
27025             
27026             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27027             
27028             this.imageEl.src = src;
27029         }
27030     },
27031     
27032     onLoadCanvas : function()
27033     {   
27034         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27035         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27036         
27037         this.bodyEl.un('click', this.beforeSelectFile, this);
27038         
27039         this.notifyEl.hide();
27040         this.thumbEl.show();
27041         this.footerEl.show();
27042         
27043         this.baseRotateLevel();
27044         
27045         if(this.isDocument){
27046             this.setThumbBoxSize();
27047         }
27048         
27049         this.setThumbBoxPosition();
27050         
27051         this.baseScaleLevel();
27052         
27053         this.draw();
27054         
27055         this.resize();
27056         
27057         this.canvasLoaded = true;
27058         
27059         if(this.loadMask){
27060             this.maskEl.unmask();
27061         }
27062         
27063     },
27064     
27065     setCanvasPosition : function()
27066     {   
27067         if(!this.canvasEl){
27068             return;
27069         }
27070         
27071         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27072         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27073         
27074         this.previewEl.setLeft(pw);
27075         this.previewEl.setTop(ph);
27076         
27077     },
27078     
27079     onMouseDown : function(e)
27080     {   
27081         e.stopEvent();
27082         
27083         this.dragable = true;
27084         this.pinching = false;
27085         
27086         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27087             this.dragable = false;
27088             return;
27089         }
27090         
27091         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27092         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27093         
27094     },
27095     
27096     onMouseMove : function(e)
27097     {   
27098         e.stopEvent();
27099         
27100         if(!this.canvasLoaded){
27101             return;
27102         }
27103         
27104         if (!this.dragable){
27105             return;
27106         }
27107         
27108         var minX = Math.ceil(this.thumbEl.getLeft(true));
27109         var minY = Math.ceil(this.thumbEl.getTop(true));
27110         
27111         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27112         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27113         
27114         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27115         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27116         
27117         x = x - this.mouseX;
27118         y = y - this.mouseY;
27119         
27120         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27121         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27122         
27123         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27124         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27125         
27126         this.previewEl.setLeft(bgX);
27127         this.previewEl.setTop(bgY);
27128         
27129         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27130         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27131     },
27132     
27133     onMouseUp : function(e)
27134     {   
27135         e.stopEvent();
27136         
27137         this.dragable = false;
27138     },
27139     
27140     onMouseWheel : function(e)
27141     {   
27142         e.stopEvent();
27143         
27144         this.startScale = this.scale;
27145         
27146         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27147         
27148         if(!this.zoomable()){
27149             this.scale = this.startScale;
27150             return;
27151         }
27152         
27153         this.draw();
27154         
27155         return;
27156     },
27157     
27158     zoomable : function()
27159     {
27160         var minScale = this.thumbEl.getWidth() / this.minWidth;
27161         
27162         if(this.minWidth < this.minHeight){
27163             minScale = this.thumbEl.getHeight() / this.minHeight;
27164         }
27165         
27166         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27167         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27168         
27169         if(
27170                 this.isDocument &&
27171                 (this.rotate == 0 || this.rotate == 180) && 
27172                 (
27173                     width > this.imageEl.OriginWidth || 
27174                     height > this.imageEl.OriginHeight ||
27175                     (width < this.minWidth && height < this.minHeight)
27176                 )
27177         ){
27178             return false;
27179         }
27180         
27181         if(
27182                 this.isDocument &&
27183                 (this.rotate == 90 || this.rotate == 270) && 
27184                 (
27185                     width > this.imageEl.OriginWidth || 
27186                     height > this.imageEl.OriginHeight ||
27187                     (width < this.minHeight && height < this.minWidth)
27188                 )
27189         ){
27190             return false;
27191         }
27192         
27193         if(
27194                 !this.isDocument &&
27195                 (this.rotate == 0 || this.rotate == 180) && 
27196                 (
27197                     width < this.minWidth || 
27198                     width > this.imageEl.OriginWidth || 
27199                     height < this.minHeight || 
27200                     height > this.imageEl.OriginHeight
27201                 )
27202         ){
27203             return false;
27204         }
27205         
27206         if(
27207                 !this.isDocument &&
27208                 (this.rotate == 90 || this.rotate == 270) && 
27209                 (
27210                     width < this.minHeight || 
27211                     width > this.imageEl.OriginWidth || 
27212                     height < this.minWidth || 
27213                     height > this.imageEl.OriginHeight
27214                 )
27215         ){
27216             return false;
27217         }
27218         
27219         return true;
27220         
27221     },
27222     
27223     onRotateLeft : function(e)
27224     {   
27225         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27226             
27227             var minScale = this.thumbEl.getWidth() / this.minWidth;
27228             
27229             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27230             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27231             
27232             this.startScale = this.scale;
27233             
27234             while (this.getScaleLevel() < minScale){
27235             
27236                 this.scale = this.scale + 1;
27237                 
27238                 if(!this.zoomable()){
27239                     break;
27240                 }
27241                 
27242                 if(
27243                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27244                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27245                 ){
27246                     continue;
27247                 }
27248                 
27249                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27250
27251                 this.draw();
27252                 
27253                 return;
27254             }
27255             
27256             this.scale = this.startScale;
27257             
27258             this.onRotateFail();
27259             
27260             return false;
27261         }
27262         
27263         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27264
27265         if(this.isDocument){
27266             this.setThumbBoxSize();
27267             this.setThumbBoxPosition();
27268             this.setCanvasPosition();
27269         }
27270         
27271         this.draw();
27272         
27273         this.fireEvent('rotate', this, 'left');
27274         
27275     },
27276     
27277     onRotateRight : function(e)
27278     {
27279         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27280             
27281             var minScale = this.thumbEl.getWidth() / this.minWidth;
27282         
27283             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27284             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27285             
27286             this.startScale = this.scale;
27287             
27288             while (this.getScaleLevel() < minScale){
27289             
27290                 this.scale = this.scale + 1;
27291                 
27292                 if(!this.zoomable()){
27293                     break;
27294                 }
27295                 
27296                 if(
27297                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27298                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27299                 ){
27300                     continue;
27301                 }
27302                 
27303                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27304
27305                 this.draw();
27306                 
27307                 return;
27308             }
27309             
27310             this.scale = this.startScale;
27311             
27312             this.onRotateFail();
27313             
27314             return false;
27315         }
27316         
27317         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27318
27319         if(this.isDocument){
27320             this.setThumbBoxSize();
27321             this.setThumbBoxPosition();
27322             this.setCanvasPosition();
27323         }
27324         
27325         this.draw();
27326         
27327         this.fireEvent('rotate', this, 'right');
27328     },
27329     
27330     onRotateFail : function()
27331     {
27332         this.errorEl.show(true);
27333         
27334         var _this = this;
27335         
27336         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27337     },
27338     
27339     draw : function()
27340     {
27341         this.previewEl.dom.innerHTML = '';
27342         
27343         var canvasEl = document.createElement("canvas");
27344         
27345         var contextEl = canvasEl.getContext("2d");
27346         
27347         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27348         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27349         var center = this.imageEl.OriginWidth / 2;
27350         
27351         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27352             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27353             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27354             center = this.imageEl.OriginHeight / 2;
27355         }
27356         
27357         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27358         
27359         contextEl.translate(center, center);
27360         contextEl.rotate(this.rotate * Math.PI / 180);
27361
27362         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27363         
27364         this.canvasEl = document.createElement("canvas");
27365         
27366         this.contextEl = this.canvasEl.getContext("2d");
27367         
27368         switch (this.rotate) {
27369             case 0 :
27370                 
27371                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27372                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27373                 
27374                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27375                 
27376                 break;
27377             case 90 : 
27378                 
27379                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27380                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27381                 
27382                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27383                     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);
27384                     break;
27385                 }
27386                 
27387                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27388                 
27389                 break;
27390             case 180 :
27391                 
27392                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27393                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27394                 
27395                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27396                     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);
27397                     break;
27398                 }
27399                 
27400                 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);
27401                 
27402                 break;
27403             case 270 :
27404                 
27405                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27406                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27407         
27408                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27409                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27410                     break;
27411                 }
27412                 
27413                 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);
27414                 
27415                 break;
27416             default : 
27417                 break;
27418         }
27419         
27420         this.previewEl.appendChild(this.canvasEl);
27421         
27422         this.setCanvasPosition();
27423     },
27424     
27425     crop : function()
27426     {
27427         if(!this.canvasLoaded){
27428             return;
27429         }
27430         
27431         var imageCanvas = document.createElement("canvas");
27432         
27433         var imageContext = imageCanvas.getContext("2d");
27434         
27435         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27436         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27437         
27438         var center = imageCanvas.width / 2;
27439         
27440         imageContext.translate(center, center);
27441         
27442         imageContext.rotate(this.rotate * Math.PI / 180);
27443         
27444         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27445         
27446         var canvas = document.createElement("canvas");
27447         
27448         var context = canvas.getContext("2d");
27449                 
27450         canvas.width = this.minWidth;
27451         canvas.height = this.minHeight;
27452
27453         switch (this.rotate) {
27454             case 0 :
27455                 
27456                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27457                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27458                 
27459                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27460                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27461                 
27462                 var targetWidth = this.minWidth - 2 * x;
27463                 var targetHeight = this.minHeight - 2 * y;
27464                 
27465                 var scale = 1;
27466                 
27467                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27468                     scale = targetWidth / width;
27469                 }
27470                 
27471                 if(x > 0 && y == 0){
27472                     scale = targetHeight / height;
27473                 }
27474                 
27475                 if(x > 0 && y > 0){
27476                     scale = targetWidth / width;
27477                     
27478                     if(width < height){
27479                         scale = targetHeight / height;
27480                     }
27481                 }
27482                 
27483                 context.scale(scale, scale);
27484                 
27485                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27486                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27487
27488                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27489                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27490
27491                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27492                 
27493                 break;
27494             case 90 : 
27495                 
27496                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27497                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27498                 
27499                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27500                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27501                 
27502                 var targetWidth = this.minWidth - 2 * x;
27503                 var targetHeight = this.minHeight - 2 * y;
27504                 
27505                 var scale = 1;
27506                 
27507                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27508                     scale = targetWidth / width;
27509                 }
27510                 
27511                 if(x > 0 && y == 0){
27512                     scale = targetHeight / height;
27513                 }
27514                 
27515                 if(x > 0 && y > 0){
27516                     scale = targetWidth / width;
27517                     
27518                     if(width < height){
27519                         scale = targetHeight / height;
27520                     }
27521                 }
27522                 
27523                 context.scale(scale, scale);
27524                 
27525                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27526                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27527
27528                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27529                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27530                 
27531                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27532                 
27533                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27534                 
27535                 break;
27536             case 180 :
27537                 
27538                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27539                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27540                 
27541                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27542                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27543                 
27544                 var targetWidth = this.minWidth - 2 * x;
27545                 var targetHeight = this.minHeight - 2 * y;
27546                 
27547                 var scale = 1;
27548                 
27549                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27550                     scale = targetWidth / width;
27551                 }
27552                 
27553                 if(x > 0 && y == 0){
27554                     scale = targetHeight / height;
27555                 }
27556                 
27557                 if(x > 0 && y > 0){
27558                     scale = targetWidth / width;
27559                     
27560                     if(width < height){
27561                         scale = targetHeight / height;
27562                     }
27563                 }
27564                 
27565                 context.scale(scale, scale);
27566                 
27567                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27568                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27569
27570                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27571                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27572
27573                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27574                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27575                 
27576                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27577                 
27578                 break;
27579             case 270 :
27580                 
27581                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27582                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27583                 
27584                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27585                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27586                 
27587                 var targetWidth = this.minWidth - 2 * x;
27588                 var targetHeight = this.minHeight - 2 * y;
27589                 
27590                 var scale = 1;
27591                 
27592                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27593                     scale = targetWidth / width;
27594                 }
27595                 
27596                 if(x > 0 && y == 0){
27597                     scale = targetHeight / height;
27598                 }
27599                 
27600                 if(x > 0 && y > 0){
27601                     scale = targetWidth / width;
27602                     
27603                     if(width < height){
27604                         scale = targetHeight / height;
27605                     }
27606                 }
27607                 
27608                 context.scale(scale, scale);
27609                 
27610                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27611                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27612
27613                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27614                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27615                 
27616                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27617                 
27618                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27619                 
27620                 break;
27621             default : 
27622                 break;
27623         }
27624         
27625         this.cropData = canvas.toDataURL(this.cropType);
27626         
27627         if(this.fireEvent('crop', this, this.cropData) !== false){
27628             this.process(this.file, this.cropData);
27629         }
27630         
27631         return;
27632         
27633     },
27634     
27635     setThumbBoxSize : function()
27636     {
27637         var width, height;
27638         
27639         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27640             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27641             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27642             
27643             this.minWidth = width;
27644             this.minHeight = height;
27645             
27646             if(this.rotate == 90 || this.rotate == 270){
27647                 this.minWidth = height;
27648                 this.minHeight = width;
27649             }
27650         }
27651         
27652         height = 300;
27653         width = Math.ceil(this.minWidth * height / this.minHeight);
27654         
27655         if(this.minWidth > this.minHeight){
27656             width = 300;
27657             height = Math.ceil(this.minHeight * width / this.minWidth);
27658         }
27659         
27660         this.thumbEl.setStyle({
27661             width : width + 'px',
27662             height : height + 'px'
27663         });
27664
27665         return;
27666             
27667     },
27668     
27669     setThumbBoxPosition : function()
27670     {
27671         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27672         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27673         
27674         this.thumbEl.setLeft(x);
27675         this.thumbEl.setTop(y);
27676         
27677     },
27678     
27679     baseRotateLevel : function()
27680     {
27681         this.baseRotate = 1;
27682         
27683         if(
27684                 typeof(this.exif) != 'undefined' &&
27685                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27686                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27687         ){
27688             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27689         }
27690         
27691         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27692         
27693     },
27694     
27695     baseScaleLevel : function()
27696     {
27697         var width, height;
27698         
27699         if(this.isDocument){
27700             
27701             if(this.baseRotate == 6 || this.baseRotate == 8){
27702             
27703                 height = this.thumbEl.getHeight();
27704                 this.baseScale = height / this.imageEl.OriginWidth;
27705
27706                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27707                     width = this.thumbEl.getWidth();
27708                     this.baseScale = width / this.imageEl.OriginHeight;
27709                 }
27710
27711                 return;
27712             }
27713
27714             height = this.thumbEl.getHeight();
27715             this.baseScale = height / this.imageEl.OriginHeight;
27716
27717             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27718                 width = this.thumbEl.getWidth();
27719                 this.baseScale = width / this.imageEl.OriginWidth;
27720             }
27721
27722             return;
27723         }
27724         
27725         if(this.baseRotate == 6 || this.baseRotate == 8){
27726             
27727             width = this.thumbEl.getHeight();
27728             this.baseScale = width / this.imageEl.OriginHeight;
27729             
27730             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27731                 height = this.thumbEl.getWidth();
27732                 this.baseScale = height / this.imageEl.OriginHeight;
27733             }
27734             
27735             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27736                 height = this.thumbEl.getWidth();
27737                 this.baseScale = height / this.imageEl.OriginHeight;
27738                 
27739                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27740                     width = this.thumbEl.getHeight();
27741                     this.baseScale = width / this.imageEl.OriginWidth;
27742                 }
27743             }
27744             
27745             return;
27746         }
27747         
27748         width = this.thumbEl.getWidth();
27749         this.baseScale = width / this.imageEl.OriginWidth;
27750         
27751         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27752             height = this.thumbEl.getHeight();
27753             this.baseScale = height / this.imageEl.OriginHeight;
27754         }
27755         
27756         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27757             
27758             height = this.thumbEl.getHeight();
27759             this.baseScale = height / this.imageEl.OriginHeight;
27760             
27761             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27762                 width = this.thumbEl.getWidth();
27763                 this.baseScale = width / this.imageEl.OriginWidth;
27764             }
27765             
27766         }
27767         
27768         return;
27769     },
27770     
27771     getScaleLevel : function()
27772     {
27773         return this.baseScale * Math.pow(1.1, this.scale);
27774     },
27775     
27776     onTouchStart : function(e)
27777     {
27778         if(!this.canvasLoaded){
27779             this.beforeSelectFile(e);
27780             return;
27781         }
27782         
27783         var touches = e.browserEvent.touches;
27784         
27785         if(!touches){
27786             return;
27787         }
27788         
27789         if(touches.length == 1){
27790             this.onMouseDown(e);
27791             return;
27792         }
27793         
27794         if(touches.length != 2){
27795             return;
27796         }
27797         
27798         var coords = [];
27799         
27800         for(var i = 0, finger; finger = touches[i]; i++){
27801             coords.push(finger.pageX, finger.pageY);
27802         }
27803         
27804         var x = Math.pow(coords[0] - coords[2], 2);
27805         var y = Math.pow(coords[1] - coords[3], 2);
27806         
27807         this.startDistance = Math.sqrt(x + y);
27808         
27809         this.startScale = this.scale;
27810         
27811         this.pinching = true;
27812         this.dragable = false;
27813         
27814     },
27815     
27816     onTouchMove : function(e)
27817     {
27818         if(!this.pinching && !this.dragable){
27819             return;
27820         }
27821         
27822         var touches = e.browserEvent.touches;
27823         
27824         if(!touches){
27825             return;
27826         }
27827         
27828         if(this.dragable){
27829             this.onMouseMove(e);
27830             return;
27831         }
27832         
27833         var coords = [];
27834         
27835         for(var i = 0, finger; finger = touches[i]; i++){
27836             coords.push(finger.pageX, finger.pageY);
27837         }
27838         
27839         var x = Math.pow(coords[0] - coords[2], 2);
27840         var y = Math.pow(coords[1] - coords[3], 2);
27841         
27842         this.endDistance = Math.sqrt(x + y);
27843         
27844         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27845         
27846         if(!this.zoomable()){
27847             this.scale = this.startScale;
27848             return;
27849         }
27850         
27851         this.draw();
27852         
27853     },
27854     
27855     onTouchEnd : function(e)
27856     {
27857         this.pinching = false;
27858         this.dragable = false;
27859         
27860     },
27861     
27862     process : function(file, crop)
27863     {
27864         if(this.loadMask){
27865             this.maskEl.mask(this.loadingText);
27866         }
27867         
27868         this.xhr = new XMLHttpRequest();
27869         
27870         file.xhr = this.xhr;
27871
27872         this.xhr.open(this.method, this.url, true);
27873         
27874         var headers = {
27875             "Accept": "application/json",
27876             "Cache-Control": "no-cache",
27877             "X-Requested-With": "XMLHttpRequest"
27878         };
27879         
27880         for (var headerName in headers) {
27881             var headerValue = headers[headerName];
27882             if (headerValue) {
27883                 this.xhr.setRequestHeader(headerName, headerValue);
27884             }
27885         }
27886         
27887         var _this = this;
27888         
27889         this.xhr.onload = function()
27890         {
27891             _this.xhrOnLoad(_this.xhr);
27892         }
27893         
27894         this.xhr.onerror = function()
27895         {
27896             _this.xhrOnError(_this.xhr);
27897         }
27898         
27899         var formData = new FormData();
27900
27901         formData.append('returnHTML', 'NO');
27902         
27903         if(crop){
27904             formData.append('crop', crop);
27905         }
27906         
27907         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27908             formData.append(this.paramName, file, file.name);
27909         }
27910         
27911         if(typeof(file.filename) != 'undefined'){
27912             formData.append('filename', file.filename);
27913         }
27914         
27915         if(typeof(file.mimetype) != 'undefined'){
27916             formData.append('mimetype', file.mimetype);
27917         }
27918         
27919         if(this.fireEvent('arrange', this, formData) != false){
27920             this.xhr.send(formData);
27921         };
27922     },
27923     
27924     xhrOnLoad : function(xhr)
27925     {
27926         if(this.loadMask){
27927             this.maskEl.unmask();
27928         }
27929         
27930         if (xhr.readyState !== 4) {
27931             this.fireEvent('exception', this, xhr);
27932             return;
27933         }
27934
27935         var response = Roo.decode(xhr.responseText);
27936         
27937         if(!response.success){
27938             this.fireEvent('exception', this, xhr);
27939             return;
27940         }
27941         
27942         var response = Roo.decode(xhr.responseText);
27943         
27944         this.fireEvent('upload', this, response);
27945         
27946     },
27947     
27948     xhrOnError : function()
27949     {
27950         if(this.loadMask){
27951             this.maskEl.unmask();
27952         }
27953         
27954         Roo.log('xhr on error');
27955         
27956         var response = Roo.decode(xhr.responseText);
27957           
27958         Roo.log(response);
27959         
27960     },
27961     
27962     prepare : function(file)
27963     {   
27964         if(this.loadMask){
27965             this.maskEl.mask(this.loadingText);
27966         }
27967         
27968         this.file = false;
27969         this.exif = {};
27970         
27971         if(typeof(file) === 'string'){
27972             this.loadCanvas(file);
27973             return;
27974         }
27975         
27976         if(!file || !this.urlAPI){
27977             return;
27978         }
27979         
27980         this.file = file;
27981         this.cropType = file.type;
27982         
27983         var _this = this;
27984         
27985         if(this.fireEvent('prepare', this, this.file) != false){
27986             
27987             var reader = new FileReader();
27988             
27989             reader.onload = function (e) {
27990                 if (e.target.error) {
27991                     Roo.log(e.target.error);
27992                     return;
27993                 }
27994                 
27995                 var buffer = e.target.result,
27996                     dataView = new DataView(buffer),
27997                     offset = 2,
27998                     maxOffset = dataView.byteLength - 4,
27999                     markerBytes,
28000                     markerLength;
28001                 
28002                 if (dataView.getUint16(0) === 0xffd8) {
28003                     while (offset < maxOffset) {
28004                         markerBytes = dataView.getUint16(offset);
28005                         
28006                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28007                             markerLength = dataView.getUint16(offset + 2) + 2;
28008                             if (offset + markerLength > dataView.byteLength) {
28009                                 Roo.log('Invalid meta data: Invalid segment size.');
28010                                 break;
28011                             }
28012                             
28013                             if(markerBytes == 0xffe1){
28014                                 _this.parseExifData(
28015                                     dataView,
28016                                     offset,
28017                                     markerLength
28018                                 );
28019                             }
28020                             
28021                             offset += markerLength;
28022                             
28023                             continue;
28024                         }
28025                         
28026                         break;
28027                     }
28028                     
28029                 }
28030                 
28031                 var url = _this.urlAPI.createObjectURL(_this.file);
28032                 
28033                 _this.loadCanvas(url);
28034                 
28035                 return;
28036             }
28037             
28038             reader.readAsArrayBuffer(this.file);
28039             
28040         }
28041         
28042     },
28043     
28044     parseExifData : function(dataView, offset, length)
28045     {
28046         var tiffOffset = offset + 10,
28047             littleEndian,
28048             dirOffset;
28049     
28050         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28051             // No Exif data, might be XMP data instead
28052             return;
28053         }
28054         
28055         // Check for the ASCII code for "Exif" (0x45786966):
28056         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28057             // No Exif data, might be XMP data instead
28058             return;
28059         }
28060         if (tiffOffset + 8 > dataView.byteLength) {
28061             Roo.log('Invalid Exif data: Invalid segment size.');
28062             return;
28063         }
28064         // Check for the two null bytes:
28065         if (dataView.getUint16(offset + 8) !== 0x0000) {
28066             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28067             return;
28068         }
28069         // Check the byte alignment:
28070         switch (dataView.getUint16(tiffOffset)) {
28071         case 0x4949:
28072             littleEndian = true;
28073             break;
28074         case 0x4D4D:
28075             littleEndian = false;
28076             break;
28077         default:
28078             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28079             return;
28080         }
28081         // Check for the TIFF tag marker (0x002A):
28082         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28083             Roo.log('Invalid Exif data: Missing TIFF marker.');
28084             return;
28085         }
28086         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28087         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28088         
28089         this.parseExifTags(
28090             dataView,
28091             tiffOffset,
28092             tiffOffset + dirOffset,
28093             littleEndian
28094         );
28095     },
28096     
28097     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28098     {
28099         var tagsNumber,
28100             dirEndOffset,
28101             i;
28102         if (dirOffset + 6 > dataView.byteLength) {
28103             Roo.log('Invalid Exif data: Invalid directory offset.');
28104             return;
28105         }
28106         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28107         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28108         if (dirEndOffset + 4 > dataView.byteLength) {
28109             Roo.log('Invalid Exif data: Invalid directory size.');
28110             return;
28111         }
28112         for (i = 0; i < tagsNumber; i += 1) {
28113             this.parseExifTag(
28114                 dataView,
28115                 tiffOffset,
28116                 dirOffset + 2 + 12 * i, // tag offset
28117                 littleEndian
28118             );
28119         }
28120         // Return the offset to the next directory:
28121         return dataView.getUint32(dirEndOffset, littleEndian);
28122     },
28123     
28124     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28125     {
28126         var tag = dataView.getUint16(offset, littleEndian);
28127         
28128         this.exif[tag] = this.getExifValue(
28129             dataView,
28130             tiffOffset,
28131             offset,
28132             dataView.getUint16(offset + 2, littleEndian), // tag type
28133             dataView.getUint32(offset + 4, littleEndian), // tag length
28134             littleEndian
28135         );
28136     },
28137     
28138     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28139     {
28140         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28141             tagSize,
28142             dataOffset,
28143             values,
28144             i,
28145             str,
28146             c;
28147     
28148         if (!tagType) {
28149             Roo.log('Invalid Exif data: Invalid tag type.');
28150             return;
28151         }
28152         
28153         tagSize = tagType.size * length;
28154         // Determine if the value is contained in the dataOffset bytes,
28155         // or if the value at the dataOffset is a pointer to the actual data:
28156         dataOffset = tagSize > 4 ?
28157                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28158         if (dataOffset + tagSize > dataView.byteLength) {
28159             Roo.log('Invalid Exif data: Invalid data offset.');
28160             return;
28161         }
28162         if (length === 1) {
28163             return tagType.getValue(dataView, dataOffset, littleEndian);
28164         }
28165         values = [];
28166         for (i = 0; i < length; i += 1) {
28167             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28168         }
28169         
28170         if (tagType.ascii) {
28171             str = '';
28172             // Concatenate the chars:
28173             for (i = 0; i < values.length; i += 1) {
28174                 c = values[i];
28175                 // Ignore the terminating NULL byte(s):
28176                 if (c === '\u0000') {
28177                     break;
28178                 }
28179                 str += c;
28180             }
28181             return str;
28182         }
28183         return values;
28184     }
28185     
28186 });
28187
28188 Roo.apply(Roo.bootstrap.UploadCropbox, {
28189     tags : {
28190         'Orientation': 0x0112
28191     },
28192     
28193     Orientation: {
28194             1: 0, //'top-left',
28195 //            2: 'top-right',
28196             3: 180, //'bottom-right',
28197 //            4: 'bottom-left',
28198 //            5: 'left-top',
28199             6: 90, //'right-top',
28200 //            7: 'right-bottom',
28201             8: 270 //'left-bottom'
28202     },
28203     
28204     exifTagTypes : {
28205         // byte, 8-bit unsigned int:
28206         1: {
28207             getValue: function (dataView, dataOffset) {
28208                 return dataView.getUint8(dataOffset);
28209             },
28210             size: 1
28211         },
28212         // ascii, 8-bit byte:
28213         2: {
28214             getValue: function (dataView, dataOffset) {
28215                 return String.fromCharCode(dataView.getUint8(dataOffset));
28216             },
28217             size: 1,
28218             ascii: true
28219         },
28220         // short, 16 bit int:
28221         3: {
28222             getValue: function (dataView, dataOffset, littleEndian) {
28223                 return dataView.getUint16(dataOffset, littleEndian);
28224             },
28225             size: 2
28226         },
28227         // long, 32 bit int:
28228         4: {
28229             getValue: function (dataView, dataOffset, littleEndian) {
28230                 return dataView.getUint32(dataOffset, littleEndian);
28231             },
28232             size: 4
28233         },
28234         // rational = two long values, first is numerator, second is denominator:
28235         5: {
28236             getValue: function (dataView, dataOffset, littleEndian) {
28237                 return dataView.getUint32(dataOffset, littleEndian) /
28238                     dataView.getUint32(dataOffset + 4, littleEndian);
28239             },
28240             size: 8
28241         },
28242         // slong, 32 bit signed int:
28243         9: {
28244             getValue: function (dataView, dataOffset, littleEndian) {
28245                 return dataView.getInt32(dataOffset, littleEndian);
28246             },
28247             size: 4
28248         },
28249         // srational, two slongs, first is numerator, second is denominator:
28250         10: {
28251             getValue: function (dataView, dataOffset, littleEndian) {
28252                 return dataView.getInt32(dataOffset, littleEndian) /
28253                     dataView.getInt32(dataOffset + 4, littleEndian);
28254             },
28255             size: 8
28256         }
28257     },
28258     
28259     footer : {
28260         STANDARD : [
28261             {
28262                 tag : 'div',
28263                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28264                 action : 'rotate-left',
28265                 cn : [
28266                     {
28267                         tag : 'button',
28268                         cls : 'btn btn-default',
28269                         html : '<i class="fa fa-undo"></i>'
28270                     }
28271                 ]
28272             },
28273             {
28274                 tag : 'div',
28275                 cls : 'btn-group roo-upload-cropbox-picture',
28276                 action : 'picture',
28277                 cn : [
28278                     {
28279                         tag : 'button',
28280                         cls : 'btn btn-default',
28281                         html : '<i class="fa fa-picture-o"></i>'
28282                     }
28283                 ]
28284             },
28285             {
28286                 tag : 'div',
28287                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28288                 action : 'rotate-right',
28289                 cn : [
28290                     {
28291                         tag : 'button',
28292                         cls : 'btn btn-default',
28293                         html : '<i class="fa fa-repeat"></i>'
28294                     }
28295                 ]
28296             }
28297         ],
28298         DOCUMENT : [
28299             {
28300                 tag : 'div',
28301                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28302                 action : 'rotate-left',
28303                 cn : [
28304                     {
28305                         tag : 'button',
28306                         cls : 'btn btn-default',
28307                         html : '<i class="fa fa-undo"></i>'
28308                     }
28309                 ]
28310             },
28311             {
28312                 tag : 'div',
28313                 cls : 'btn-group roo-upload-cropbox-download',
28314                 action : 'download',
28315                 cn : [
28316                     {
28317                         tag : 'button',
28318                         cls : 'btn btn-default',
28319                         html : '<i class="fa fa-download"></i>'
28320                     }
28321                 ]
28322             },
28323             {
28324                 tag : 'div',
28325                 cls : 'btn-group roo-upload-cropbox-crop',
28326                 action : 'crop',
28327                 cn : [
28328                     {
28329                         tag : 'button',
28330                         cls : 'btn btn-default',
28331                         html : '<i class="fa fa-crop"></i>'
28332                     }
28333                 ]
28334             },
28335             {
28336                 tag : 'div',
28337                 cls : 'btn-group roo-upload-cropbox-trash',
28338                 action : 'trash',
28339                 cn : [
28340                     {
28341                         tag : 'button',
28342                         cls : 'btn btn-default',
28343                         html : '<i class="fa fa-trash"></i>'
28344                     }
28345                 ]
28346             },
28347             {
28348                 tag : 'div',
28349                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28350                 action : 'rotate-right',
28351                 cn : [
28352                     {
28353                         tag : 'button',
28354                         cls : 'btn btn-default',
28355                         html : '<i class="fa fa-repeat"></i>'
28356                     }
28357                 ]
28358             }
28359         ],
28360         ROTATOR : [
28361             {
28362                 tag : 'div',
28363                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28364                 action : 'rotate-left',
28365                 cn : [
28366                     {
28367                         tag : 'button',
28368                         cls : 'btn btn-default',
28369                         html : '<i class="fa fa-undo"></i>'
28370                     }
28371                 ]
28372             },
28373             {
28374                 tag : 'div',
28375                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28376                 action : 'rotate-right',
28377                 cn : [
28378                     {
28379                         tag : 'button',
28380                         cls : 'btn btn-default',
28381                         html : '<i class="fa fa-repeat"></i>'
28382                     }
28383                 ]
28384             }
28385         ]
28386     }
28387 });
28388
28389 /*
28390 * Licence: LGPL
28391 */
28392
28393 /**
28394  * @class Roo.bootstrap.DocumentManager
28395  * @extends Roo.bootstrap.Component
28396  * Bootstrap DocumentManager class
28397  * @cfg {String} paramName default 'imageUpload'
28398  * @cfg {String} toolTipName default 'filename'
28399  * @cfg {String} method default POST
28400  * @cfg {String} url action url
28401  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28402  * @cfg {Boolean} multiple multiple upload default true
28403  * @cfg {Number} thumbSize default 300
28404  * @cfg {String} fieldLabel
28405  * @cfg {Number} labelWidth default 4
28406  * @cfg {String} labelAlign (left|top) default left
28407  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28408 * @cfg {Number} labellg set the width of label (1-12)
28409  * @cfg {Number} labelmd set the width of label (1-12)
28410  * @cfg {Number} labelsm set the width of label (1-12)
28411  * @cfg {Number} labelxs set the width of label (1-12)
28412  * 
28413  * @constructor
28414  * Create a new DocumentManager
28415  * @param {Object} config The config object
28416  */
28417
28418 Roo.bootstrap.DocumentManager = function(config){
28419     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28420     
28421     this.files = [];
28422     this.delegates = [];
28423     
28424     this.addEvents({
28425         /**
28426          * @event initial
28427          * Fire when initial the DocumentManager
28428          * @param {Roo.bootstrap.DocumentManager} this
28429          */
28430         "initial" : true,
28431         /**
28432          * @event inspect
28433          * inspect selected file
28434          * @param {Roo.bootstrap.DocumentManager} this
28435          * @param {File} file
28436          */
28437         "inspect" : true,
28438         /**
28439          * @event exception
28440          * Fire when xhr load exception
28441          * @param {Roo.bootstrap.DocumentManager} this
28442          * @param {XMLHttpRequest} xhr
28443          */
28444         "exception" : true,
28445         /**
28446          * @event afterupload
28447          * Fire when xhr load exception
28448          * @param {Roo.bootstrap.DocumentManager} this
28449          * @param {XMLHttpRequest} xhr
28450          */
28451         "afterupload" : true,
28452         /**
28453          * @event prepare
28454          * prepare the form data
28455          * @param {Roo.bootstrap.DocumentManager} this
28456          * @param {Object} formData
28457          */
28458         "prepare" : true,
28459         /**
28460          * @event remove
28461          * Fire when remove the file
28462          * @param {Roo.bootstrap.DocumentManager} this
28463          * @param {Object} file
28464          */
28465         "remove" : true,
28466         /**
28467          * @event refresh
28468          * Fire after refresh the file
28469          * @param {Roo.bootstrap.DocumentManager} this
28470          */
28471         "refresh" : true,
28472         /**
28473          * @event click
28474          * Fire after click the image
28475          * @param {Roo.bootstrap.DocumentManager} this
28476          * @param {Object} file
28477          */
28478         "click" : true,
28479         /**
28480          * @event edit
28481          * Fire when upload a image and editable set to true
28482          * @param {Roo.bootstrap.DocumentManager} this
28483          * @param {Object} file
28484          */
28485         "edit" : true,
28486         /**
28487          * @event beforeselectfile
28488          * Fire before select file
28489          * @param {Roo.bootstrap.DocumentManager} this
28490          */
28491         "beforeselectfile" : true,
28492         /**
28493          * @event process
28494          * Fire before process file
28495          * @param {Roo.bootstrap.DocumentManager} this
28496          * @param {Object} file
28497          */
28498         "process" : true,
28499         /**
28500          * @event previewrendered
28501          * Fire when preview rendered
28502          * @param {Roo.bootstrap.DocumentManager} this
28503          * @param {Object} file
28504          */
28505         "previewrendered" : true
28506         
28507     });
28508 };
28509
28510 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28511     
28512     boxes : 0,
28513     inputName : '',
28514     thumbSize : 300,
28515     multiple : true,
28516     files : false,
28517     method : 'POST',
28518     url : '',
28519     paramName : 'imageUpload',
28520     toolTipName : 'filename',
28521     fieldLabel : '',
28522     labelWidth : 4,
28523     labelAlign : 'left',
28524     editable : true,
28525     delegates : false,
28526     xhr : false, 
28527     
28528     labellg : 0,
28529     labelmd : 0,
28530     labelsm : 0,
28531     labelxs : 0,
28532     
28533     getAutoCreate : function()
28534     {   
28535         var managerWidget = {
28536             tag : 'div',
28537             cls : 'roo-document-manager',
28538             cn : [
28539                 {
28540                     tag : 'input',
28541                     cls : 'roo-document-manager-selector',
28542                     type : 'file'
28543                 },
28544                 {
28545                     tag : 'div',
28546                     cls : 'roo-document-manager-uploader',
28547                     cn : [
28548                         {
28549                             tag : 'div',
28550                             cls : 'roo-document-manager-upload-btn',
28551                             html : '<i class="fa fa-plus"></i>'
28552                         }
28553                     ]
28554                     
28555                 }
28556             ]
28557         };
28558         
28559         var content = [
28560             {
28561                 tag : 'div',
28562                 cls : 'column col-md-12',
28563                 cn : managerWidget
28564             }
28565         ];
28566         
28567         if(this.fieldLabel.length){
28568             
28569             content = [
28570                 {
28571                     tag : 'div',
28572                     cls : 'column col-md-12',
28573                     html : this.fieldLabel
28574                 },
28575                 {
28576                     tag : 'div',
28577                     cls : 'column col-md-12',
28578                     cn : managerWidget
28579                 }
28580             ];
28581
28582             if(this.labelAlign == 'left'){
28583                 content = [
28584                     {
28585                         tag : 'div',
28586                         cls : 'column',
28587                         html : this.fieldLabel
28588                     },
28589                     {
28590                         tag : 'div',
28591                         cls : 'column',
28592                         cn : managerWidget
28593                     }
28594                 ];
28595                 
28596                 if(this.labelWidth > 12){
28597                     content[0].style = "width: " + this.labelWidth + 'px';
28598                 }
28599
28600                 if(this.labelWidth < 13 && this.labelmd == 0){
28601                     this.labelmd = this.labelWidth;
28602                 }
28603
28604                 if(this.labellg > 0){
28605                     content[0].cls += ' col-lg-' + this.labellg;
28606                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28607                 }
28608
28609                 if(this.labelmd > 0){
28610                     content[0].cls += ' col-md-' + this.labelmd;
28611                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28612                 }
28613
28614                 if(this.labelsm > 0){
28615                     content[0].cls += ' col-sm-' + this.labelsm;
28616                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28617                 }
28618
28619                 if(this.labelxs > 0){
28620                     content[0].cls += ' col-xs-' + this.labelxs;
28621                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28622                 }
28623                 
28624             }
28625         }
28626         
28627         var cfg = {
28628             tag : 'div',
28629             cls : 'row clearfix',
28630             cn : content
28631         };
28632         
28633         return cfg;
28634         
28635     },
28636     
28637     initEvents : function()
28638     {
28639         this.managerEl = this.el.select('.roo-document-manager', true).first();
28640         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28641         
28642         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28643         this.selectorEl.hide();
28644         
28645         if(this.multiple){
28646             this.selectorEl.attr('multiple', 'multiple');
28647         }
28648         
28649         this.selectorEl.on('change', this.onFileSelected, this);
28650         
28651         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28652         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28653         
28654         this.uploader.on('click', this.onUploaderClick, this);
28655         
28656         this.renderProgressDialog();
28657         
28658         var _this = this;
28659         
28660         window.addEventListener("resize", function() { _this.refresh(); } );
28661         
28662         this.fireEvent('initial', this);
28663     },
28664     
28665     renderProgressDialog : function()
28666     {
28667         var _this = this;
28668         
28669         this.progressDialog = new Roo.bootstrap.Modal({
28670             cls : 'roo-document-manager-progress-dialog',
28671             allow_close : false,
28672             title : '',
28673             buttons : [
28674                 {
28675                     name  :'cancel',
28676                     weight : 'danger',
28677                     html : 'Cancel'
28678                 }
28679             ], 
28680             listeners : { 
28681                 btnclick : function() {
28682                     _this.uploadCancel();
28683                     this.hide();
28684                 }
28685             }
28686         });
28687          
28688         this.progressDialog.render(Roo.get(document.body));
28689          
28690         this.progress = new Roo.bootstrap.Progress({
28691             cls : 'roo-document-manager-progress',
28692             active : true,
28693             striped : true
28694         });
28695         
28696         this.progress.render(this.progressDialog.getChildContainer());
28697         
28698         this.progressBar = new Roo.bootstrap.ProgressBar({
28699             cls : 'roo-document-manager-progress-bar',
28700             aria_valuenow : 0,
28701             aria_valuemin : 0,
28702             aria_valuemax : 12,
28703             panel : 'success'
28704         });
28705         
28706         this.progressBar.render(this.progress.getChildContainer());
28707     },
28708     
28709     onUploaderClick : function(e)
28710     {
28711         e.preventDefault();
28712      
28713         if(this.fireEvent('beforeselectfile', this) != false){
28714             this.selectorEl.dom.click();
28715         }
28716         
28717     },
28718     
28719     onFileSelected : function(e)
28720     {
28721         e.preventDefault();
28722         
28723         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28724             return;
28725         }
28726         
28727         Roo.each(this.selectorEl.dom.files, function(file){
28728             if(this.fireEvent('inspect', this, file) != false){
28729                 this.files.push(file);
28730             }
28731         }, this);
28732         
28733         this.queue();
28734         
28735     },
28736     
28737     queue : function()
28738     {
28739         this.selectorEl.dom.value = '';
28740         
28741         if(!this.files || !this.files.length){
28742             return;
28743         }
28744         
28745         if(this.boxes > 0 && this.files.length > this.boxes){
28746             this.files = this.files.slice(0, this.boxes);
28747         }
28748         
28749         this.uploader.show();
28750         
28751         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28752             this.uploader.hide();
28753         }
28754         
28755         var _this = this;
28756         
28757         var files = [];
28758         
28759         var docs = [];
28760         
28761         Roo.each(this.files, function(file){
28762             
28763             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28764                 var f = this.renderPreview(file);
28765                 files.push(f);
28766                 return;
28767             }
28768             
28769             if(file.type.indexOf('image') != -1){
28770                 this.delegates.push(
28771                     (function(){
28772                         _this.process(file);
28773                     }).createDelegate(this)
28774                 );
28775         
28776                 return;
28777             }
28778             
28779             docs.push(
28780                 (function(){
28781                     _this.process(file);
28782                 }).createDelegate(this)
28783             );
28784             
28785         }, this);
28786         
28787         this.files = files;
28788         
28789         this.delegates = this.delegates.concat(docs);
28790         
28791         if(!this.delegates.length){
28792             this.refresh();
28793             return;
28794         }
28795         
28796         this.progressBar.aria_valuemax = this.delegates.length;
28797         
28798         this.arrange();
28799         
28800         return;
28801     },
28802     
28803     arrange : function()
28804     {
28805         if(!this.delegates.length){
28806             this.progressDialog.hide();
28807             this.refresh();
28808             return;
28809         }
28810         
28811         var delegate = this.delegates.shift();
28812         
28813         this.progressDialog.show();
28814         
28815         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28816         
28817         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28818         
28819         delegate();
28820     },
28821     
28822     refresh : function()
28823     {
28824         this.uploader.show();
28825         
28826         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28827             this.uploader.hide();
28828         }
28829         
28830         Roo.isTouch ? this.closable(false) : this.closable(true);
28831         
28832         this.fireEvent('refresh', this);
28833     },
28834     
28835     onRemove : function(e, el, o)
28836     {
28837         e.preventDefault();
28838         
28839         this.fireEvent('remove', this, o);
28840         
28841     },
28842     
28843     remove : function(o)
28844     {
28845         var files = [];
28846         
28847         Roo.each(this.files, function(file){
28848             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28849                 files.push(file);
28850                 return;
28851             }
28852
28853             o.target.remove();
28854
28855         }, this);
28856         
28857         this.files = files;
28858         
28859         this.refresh();
28860     },
28861     
28862     clear : function()
28863     {
28864         Roo.each(this.files, function(file){
28865             if(!file.target){
28866                 return;
28867             }
28868             
28869             file.target.remove();
28870
28871         }, this);
28872         
28873         this.files = [];
28874         
28875         this.refresh();
28876     },
28877     
28878     onClick : function(e, el, o)
28879     {
28880         e.preventDefault();
28881         
28882         this.fireEvent('click', this, o);
28883         
28884     },
28885     
28886     closable : function(closable)
28887     {
28888         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28889             
28890             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28891             
28892             if(closable){
28893                 el.show();
28894                 return;
28895             }
28896             
28897             el.hide();
28898             
28899         }, this);
28900     },
28901     
28902     xhrOnLoad : function(xhr)
28903     {
28904         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28905             el.remove();
28906         }, this);
28907         
28908         if (xhr.readyState !== 4) {
28909             this.arrange();
28910             this.fireEvent('exception', this, xhr);
28911             return;
28912         }
28913
28914         var response = Roo.decode(xhr.responseText);
28915         
28916         if(!response.success){
28917             this.arrange();
28918             this.fireEvent('exception', this, xhr);
28919             return;
28920         }
28921         
28922         var file = this.renderPreview(response.data);
28923         
28924         this.files.push(file);
28925         
28926         this.arrange();
28927         
28928         this.fireEvent('afterupload', this, xhr);
28929         
28930     },
28931     
28932     xhrOnError : function(xhr)
28933     {
28934         Roo.log('xhr on error');
28935         
28936         var response = Roo.decode(xhr.responseText);
28937           
28938         Roo.log(response);
28939         
28940         this.arrange();
28941     },
28942     
28943     process : function(file)
28944     {
28945         if(this.fireEvent('process', this, file) !== false){
28946             if(this.editable && file.type.indexOf('image') != -1){
28947                 this.fireEvent('edit', this, file);
28948                 return;
28949             }
28950
28951             this.uploadStart(file, false);
28952
28953             return;
28954         }
28955         
28956     },
28957     
28958     uploadStart : function(file, crop)
28959     {
28960         this.xhr = new XMLHttpRequest();
28961         
28962         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28963             this.arrange();
28964             return;
28965         }
28966         
28967         file.xhr = this.xhr;
28968             
28969         this.managerEl.createChild({
28970             tag : 'div',
28971             cls : 'roo-document-manager-loading',
28972             cn : [
28973                 {
28974                     tag : 'div',
28975                     tooltip : file.name,
28976                     cls : 'roo-document-manager-thumb',
28977                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28978                 }
28979             ]
28980
28981         });
28982
28983         this.xhr.open(this.method, this.url, true);
28984         
28985         var headers = {
28986             "Accept": "application/json",
28987             "Cache-Control": "no-cache",
28988             "X-Requested-With": "XMLHttpRequest"
28989         };
28990         
28991         for (var headerName in headers) {
28992             var headerValue = headers[headerName];
28993             if (headerValue) {
28994                 this.xhr.setRequestHeader(headerName, headerValue);
28995             }
28996         }
28997         
28998         var _this = this;
28999         
29000         this.xhr.onload = function()
29001         {
29002             _this.xhrOnLoad(_this.xhr);
29003         }
29004         
29005         this.xhr.onerror = function()
29006         {
29007             _this.xhrOnError(_this.xhr);
29008         }
29009         
29010         var formData = new FormData();
29011
29012         formData.append('returnHTML', 'NO');
29013         
29014         if(crop){
29015             formData.append('crop', crop);
29016         }
29017         
29018         formData.append(this.paramName, file, file.name);
29019         
29020         var options = {
29021             file : file, 
29022             manually : false
29023         };
29024         
29025         if(this.fireEvent('prepare', this, formData, options) != false){
29026             
29027             if(options.manually){
29028                 return;
29029             }
29030             
29031             this.xhr.send(formData);
29032             return;
29033         };
29034         
29035         this.uploadCancel();
29036     },
29037     
29038     uploadCancel : function()
29039     {
29040         if (this.xhr) {
29041             this.xhr.abort();
29042         }
29043         
29044         this.delegates = [];
29045         
29046         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29047             el.remove();
29048         }, this);
29049         
29050         this.arrange();
29051     },
29052     
29053     renderPreview : function(file)
29054     {
29055         if(typeof(file.target) != 'undefined' && file.target){
29056             return file;
29057         }
29058         
29059         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29060         
29061         var previewEl = this.managerEl.createChild({
29062             tag : 'div',
29063             cls : 'roo-document-manager-preview',
29064             cn : [
29065                 {
29066                     tag : 'div',
29067                     tooltip : file[this.toolTipName],
29068                     cls : 'roo-document-manager-thumb',
29069                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29070                 },
29071                 {
29072                     tag : 'button',
29073                     cls : 'close',
29074                     html : '<i class="fa fa-times-circle"></i>'
29075                 }
29076             ]
29077         });
29078
29079         var close = previewEl.select('button.close', true).first();
29080
29081         close.on('click', this.onRemove, this, file);
29082
29083         file.target = previewEl;
29084
29085         var image = previewEl.select('img', true).first();
29086         
29087         var _this = this;
29088         
29089         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29090         
29091         image.on('click', this.onClick, this, file);
29092         
29093         this.fireEvent('previewrendered', this, file);
29094         
29095         return file;
29096         
29097     },
29098     
29099     onPreviewLoad : function(file, image)
29100     {
29101         if(typeof(file.target) == 'undefined' || !file.target){
29102             return;
29103         }
29104         
29105         var width = image.dom.naturalWidth || image.dom.width;
29106         var height = image.dom.naturalHeight || image.dom.height;
29107         
29108         if(width > height){
29109             file.target.addClass('wide');
29110             return;
29111         }
29112         
29113         file.target.addClass('tall');
29114         return;
29115         
29116     },
29117     
29118     uploadFromSource : function(file, crop)
29119     {
29120         this.xhr = new XMLHttpRequest();
29121         
29122         this.managerEl.createChild({
29123             tag : 'div',
29124             cls : 'roo-document-manager-loading',
29125             cn : [
29126                 {
29127                     tag : 'div',
29128                     tooltip : file.name,
29129                     cls : 'roo-document-manager-thumb',
29130                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29131                 }
29132             ]
29133
29134         });
29135
29136         this.xhr.open(this.method, this.url, true);
29137         
29138         var headers = {
29139             "Accept": "application/json",
29140             "Cache-Control": "no-cache",
29141             "X-Requested-With": "XMLHttpRequest"
29142         };
29143         
29144         for (var headerName in headers) {
29145             var headerValue = headers[headerName];
29146             if (headerValue) {
29147                 this.xhr.setRequestHeader(headerName, headerValue);
29148             }
29149         }
29150         
29151         var _this = this;
29152         
29153         this.xhr.onload = function()
29154         {
29155             _this.xhrOnLoad(_this.xhr);
29156         }
29157         
29158         this.xhr.onerror = function()
29159         {
29160             _this.xhrOnError(_this.xhr);
29161         }
29162         
29163         var formData = new FormData();
29164
29165         formData.append('returnHTML', 'NO');
29166         
29167         formData.append('crop', crop);
29168         
29169         if(typeof(file.filename) != 'undefined'){
29170             formData.append('filename', file.filename);
29171         }
29172         
29173         if(typeof(file.mimetype) != 'undefined'){
29174             formData.append('mimetype', file.mimetype);
29175         }
29176         
29177         Roo.log(formData);
29178         
29179         if(this.fireEvent('prepare', this, formData) != false){
29180             this.xhr.send(formData);
29181         };
29182     }
29183 });
29184
29185 /*
29186 * Licence: LGPL
29187 */
29188
29189 /**
29190  * @class Roo.bootstrap.DocumentViewer
29191  * @extends Roo.bootstrap.Component
29192  * Bootstrap DocumentViewer class
29193  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29194  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29195  * 
29196  * @constructor
29197  * Create a new DocumentViewer
29198  * @param {Object} config The config object
29199  */
29200
29201 Roo.bootstrap.DocumentViewer = function(config){
29202     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29203     
29204     this.addEvents({
29205         /**
29206          * @event initial
29207          * Fire after initEvent
29208          * @param {Roo.bootstrap.DocumentViewer} this
29209          */
29210         "initial" : true,
29211         /**
29212          * @event click
29213          * Fire after click
29214          * @param {Roo.bootstrap.DocumentViewer} this
29215          */
29216         "click" : true,
29217         /**
29218          * @event download
29219          * Fire after download button
29220          * @param {Roo.bootstrap.DocumentViewer} this
29221          */
29222         "download" : true,
29223         /**
29224          * @event trash
29225          * Fire after trash button
29226          * @param {Roo.bootstrap.DocumentViewer} this
29227          */
29228         "trash" : true
29229         
29230     });
29231 };
29232
29233 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29234     
29235     showDownload : true,
29236     
29237     showTrash : true,
29238     
29239     getAutoCreate : function()
29240     {
29241         var cfg = {
29242             tag : 'div',
29243             cls : 'roo-document-viewer',
29244             cn : [
29245                 {
29246                     tag : 'div',
29247                     cls : 'roo-document-viewer-body',
29248                     cn : [
29249                         {
29250                             tag : 'div',
29251                             cls : 'roo-document-viewer-thumb',
29252                             cn : [
29253                                 {
29254                                     tag : 'img',
29255                                     cls : 'roo-document-viewer-image'
29256                                 }
29257                             ]
29258                         }
29259                     ]
29260                 },
29261                 {
29262                     tag : 'div',
29263                     cls : 'roo-document-viewer-footer',
29264                     cn : {
29265                         tag : 'div',
29266                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29267                         cn : [
29268                             {
29269                                 tag : 'div',
29270                                 cls : 'btn-group roo-document-viewer-download',
29271                                 cn : [
29272                                     {
29273                                         tag : 'button',
29274                                         cls : 'btn btn-default',
29275                                         html : '<i class="fa fa-download"></i>'
29276                                     }
29277                                 ]
29278                             },
29279                             {
29280                                 tag : 'div',
29281                                 cls : 'btn-group roo-document-viewer-trash',
29282                                 cn : [
29283                                     {
29284                                         tag : 'button',
29285                                         cls : 'btn btn-default',
29286                                         html : '<i class="fa fa-trash"></i>'
29287                                     }
29288                                 ]
29289                             }
29290                         ]
29291                     }
29292                 }
29293             ]
29294         };
29295         
29296         return cfg;
29297     },
29298     
29299     initEvents : function()
29300     {
29301         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29302         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29303         
29304         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29305         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29306         
29307         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29308         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29309         
29310         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29311         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29312         
29313         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29314         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29315         
29316         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29317         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29318         
29319         this.bodyEl.on('click', this.onClick, this);
29320         this.downloadBtn.on('click', this.onDownload, this);
29321         this.trashBtn.on('click', this.onTrash, this);
29322         
29323         this.downloadBtn.hide();
29324         this.trashBtn.hide();
29325         
29326         if(this.showDownload){
29327             this.downloadBtn.show();
29328         }
29329         
29330         if(this.showTrash){
29331             this.trashBtn.show();
29332         }
29333         
29334         if(!this.showDownload && !this.showTrash) {
29335             this.footerEl.hide();
29336         }
29337         
29338     },
29339     
29340     initial : function()
29341     {
29342         this.fireEvent('initial', this);
29343         
29344     },
29345     
29346     onClick : function(e)
29347     {
29348         e.preventDefault();
29349         
29350         this.fireEvent('click', this);
29351     },
29352     
29353     onDownload : function(e)
29354     {
29355         e.preventDefault();
29356         
29357         this.fireEvent('download', this);
29358     },
29359     
29360     onTrash : function(e)
29361     {
29362         e.preventDefault();
29363         
29364         this.fireEvent('trash', this);
29365     }
29366     
29367 });
29368 /*
29369  * - LGPL
29370  *
29371  * nav progress bar
29372  * 
29373  */
29374
29375 /**
29376  * @class Roo.bootstrap.NavProgressBar
29377  * @extends Roo.bootstrap.Component
29378  * Bootstrap NavProgressBar class
29379  * 
29380  * @constructor
29381  * Create a new nav progress bar
29382  * @param {Object} config The config object
29383  */
29384
29385 Roo.bootstrap.NavProgressBar = function(config){
29386     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29387
29388     this.bullets = this.bullets || [];
29389    
29390 //    Roo.bootstrap.NavProgressBar.register(this);
29391      this.addEvents({
29392         /**
29393              * @event changed
29394              * Fires when the active item changes
29395              * @param {Roo.bootstrap.NavProgressBar} this
29396              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29397              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29398          */
29399         'changed': true
29400      });
29401     
29402 };
29403
29404 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29405     
29406     bullets : [],
29407     barItems : [],
29408     
29409     getAutoCreate : function()
29410     {
29411         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29412         
29413         cfg = {
29414             tag : 'div',
29415             cls : 'roo-navigation-bar-group',
29416             cn : [
29417                 {
29418                     tag : 'div',
29419                     cls : 'roo-navigation-top-bar'
29420                 },
29421                 {
29422                     tag : 'div',
29423                     cls : 'roo-navigation-bullets-bar',
29424                     cn : [
29425                         {
29426                             tag : 'ul',
29427                             cls : 'roo-navigation-bar'
29428                         }
29429                     ]
29430                 },
29431                 
29432                 {
29433                     tag : 'div',
29434                     cls : 'roo-navigation-bottom-bar'
29435                 }
29436             ]
29437             
29438         };
29439         
29440         return cfg;
29441         
29442     },
29443     
29444     initEvents: function() 
29445     {
29446         
29447     },
29448     
29449     onRender : function(ct, position) 
29450     {
29451         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29452         
29453         if(this.bullets.length){
29454             Roo.each(this.bullets, function(b){
29455                this.addItem(b);
29456             }, this);
29457         }
29458         
29459         this.format();
29460         
29461     },
29462     
29463     addItem : function(cfg)
29464     {
29465         var item = new Roo.bootstrap.NavProgressItem(cfg);
29466         
29467         item.parentId = this.id;
29468         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29469         
29470         if(cfg.html){
29471             var top = new Roo.bootstrap.Element({
29472                 tag : 'div',
29473                 cls : 'roo-navigation-bar-text'
29474             });
29475             
29476             var bottom = new Roo.bootstrap.Element({
29477                 tag : 'div',
29478                 cls : 'roo-navigation-bar-text'
29479             });
29480             
29481             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29482             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29483             
29484             var topText = new Roo.bootstrap.Element({
29485                 tag : 'span',
29486                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29487             });
29488             
29489             var bottomText = new Roo.bootstrap.Element({
29490                 tag : 'span',
29491                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29492             });
29493             
29494             topText.onRender(top.el, null);
29495             bottomText.onRender(bottom.el, null);
29496             
29497             item.topEl = top;
29498             item.bottomEl = bottom;
29499         }
29500         
29501         this.barItems.push(item);
29502         
29503         return item;
29504     },
29505     
29506     getActive : function()
29507     {
29508         var active = false;
29509         
29510         Roo.each(this.barItems, function(v){
29511             
29512             if (!v.isActive()) {
29513                 return;
29514             }
29515             
29516             active = v;
29517             return false;
29518             
29519         });
29520         
29521         return active;
29522     },
29523     
29524     setActiveItem : function(item)
29525     {
29526         var prev = false;
29527         
29528         Roo.each(this.barItems, function(v){
29529             if (v.rid == item.rid) {
29530                 return ;
29531             }
29532             
29533             if (v.isActive()) {
29534                 v.setActive(false);
29535                 prev = v;
29536             }
29537         });
29538
29539         item.setActive(true);
29540         
29541         this.fireEvent('changed', this, item, prev);
29542     },
29543     
29544     getBarItem: function(rid)
29545     {
29546         var ret = false;
29547         
29548         Roo.each(this.barItems, function(e) {
29549             if (e.rid != rid) {
29550                 return;
29551             }
29552             
29553             ret =  e;
29554             return false;
29555         });
29556         
29557         return ret;
29558     },
29559     
29560     indexOfItem : function(item)
29561     {
29562         var index = false;
29563         
29564         Roo.each(this.barItems, function(v, i){
29565             
29566             if (v.rid != item.rid) {
29567                 return;
29568             }
29569             
29570             index = i;
29571             return false
29572         });
29573         
29574         return index;
29575     },
29576     
29577     setActiveNext : function()
29578     {
29579         var i = this.indexOfItem(this.getActive());
29580         
29581         if (i > this.barItems.length) {
29582             return;
29583         }
29584         
29585         this.setActiveItem(this.barItems[i+1]);
29586     },
29587     
29588     setActivePrev : function()
29589     {
29590         var i = this.indexOfItem(this.getActive());
29591         
29592         if (i  < 1) {
29593             return;
29594         }
29595         
29596         this.setActiveItem(this.barItems[i-1]);
29597     },
29598     
29599     format : function()
29600     {
29601         if(!this.barItems.length){
29602             return;
29603         }
29604      
29605         var width = 100 / this.barItems.length;
29606         
29607         Roo.each(this.barItems, function(i){
29608             i.el.setStyle('width', width + '%');
29609             i.topEl.el.setStyle('width', width + '%');
29610             i.bottomEl.el.setStyle('width', width + '%');
29611         }, this);
29612         
29613     }
29614     
29615 });
29616 /*
29617  * - LGPL
29618  *
29619  * Nav Progress Item
29620  * 
29621  */
29622
29623 /**
29624  * @class Roo.bootstrap.NavProgressItem
29625  * @extends Roo.bootstrap.Component
29626  * Bootstrap NavProgressItem class
29627  * @cfg {String} rid the reference id
29628  * @cfg {Boolean} active (true|false) Is item active default false
29629  * @cfg {Boolean} disabled (true|false) Is item active default false
29630  * @cfg {String} html
29631  * @cfg {String} position (top|bottom) text position default bottom
29632  * @cfg {String} icon show icon instead of number
29633  * 
29634  * @constructor
29635  * Create a new NavProgressItem
29636  * @param {Object} config The config object
29637  */
29638 Roo.bootstrap.NavProgressItem = function(config){
29639     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29640     this.addEvents({
29641         // raw events
29642         /**
29643          * @event click
29644          * The raw click event for the entire grid.
29645          * @param {Roo.bootstrap.NavProgressItem} this
29646          * @param {Roo.EventObject} e
29647          */
29648         "click" : true
29649     });
29650    
29651 };
29652
29653 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29654     
29655     rid : '',
29656     active : false,
29657     disabled : false,
29658     html : '',
29659     position : 'bottom',
29660     icon : false,
29661     
29662     getAutoCreate : function()
29663     {
29664         var iconCls = 'roo-navigation-bar-item-icon';
29665         
29666         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29667         
29668         var cfg = {
29669             tag: 'li',
29670             cls: 'roo-navigation-bar-item',
29671             cn : [
29672                 {
29673                     tag : 'i',
29674                     cls : iconCls
29675                 }
29676             ]
29677         };
29678         
29679         if(this.active){
29680             cfg.cls += ' active';
29681         }
29682         if(this.disabled){
29683             cfg.cls += ' disabled';
29684         }
29685         
29686         return cfg;
29687     },
29688     
29689     disable : function()
29690     {
29691         this.setDisabled(true);
29692     },
29693     
29694     enable : function()
29695     {
29696         this.setDisabled(false);
29697     },
29698     
29699     initEvents: function() 
29700     {
29701         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29702         
29703         this.iconEl.on('click', this.onClick, this);
29704     },
29705     
29706     onClick : function(e)
29707     {
29708         e.preventDefault();
29709         
29710         if(this.disabled){
29711             return;
29712         }
29713         
29714         if(this.fireEvent('click', this, e) === false){
29715             return;
29716         };
29717         
29718         this.parent().setActiveItem(this);
29719     },
29720     
29721     isActive: function () 
29722     {
29723         return this.active;
29724     },
29725     
29726     setActive : function(state)
29727     {
29728         if(this.active == state){
29729             return;
29730         }
29731         
29732         this.active = state;
29733         
29734         if (state) {
29735             this.el.addClass('active');
29736             return;
29737         }
29738         
29739         this.el.removeClass('active');
29740         
29741         return;
29742     },
29743     
29744     setDisabled : function(state)
29745     {
29746         if(this.disabled == state){
29747             return;
29748         }
29749         
29750         this.disabled = state;
29751         
29752         if (state) {
29753             this.el.addClass('disabled');
29754             return;
29755         }
29756         
29757         this.el.removeClass('disabled');
29758     },
29759     
29760     tooltipEl : function()
29761     {
29762         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29763     }
29764 });
29765  
29766
29767  /*
29768  * - LGPL
29769  *
29770  * FieldLabel
29771  * 
29772  */
29773
29774 /**
29775  * @class Roo.bootstrap.FieldLabel
29776  * @extends Roo.bootstrap.Component
29777  * Bootstrap FieldLabel class
29778  * @cfg {String} html contents of the element
29779  * @cfg {String} tag tag of the element default label
29780  * @cfg {String} cls class of the element
29781  * @cfg {String} target label target 
29782  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29783  * @cfg {String} invalidClass default "text-warning"
29784  * @cfg {String} validClass default "text-success"
29785  * @cfg {String} iconTooltip default "This field is required"
29786  * @cfg {String} indicatorpos (left|right) default left
29787  * 
29788  * @constructor
29789  * Create a new FieldLabel
29790  * @param {Object} config The config object
29791  */
29792
29793 Roo.bootstrap.FieldLabel = function(config){
29794     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29795     
29796     this.addEvents({
29797             /**
29798              * @event invalid
29799              * Fires after the field has been marked as invalid.
29800              * @param {Roo.form.FieldLabel} this
29801              * @param {String} msg The validation message
29802              */
29803             invalid : true,
29804             /**
29805              * @event valid
29806              * Fires after the field has been validated with no errors.
29807              * @param {Roo.form.FieldLabel} this
29808              */
29809             valid : true
29810         });
29811 };
29812
29813 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29814     
29815     tag: 'label',
29816     cls: '',
29817     html: '',
29818     target: '',
29819     allowBlank : true,
29820     invalidClass : 'has-warning',
29821     validClass : 'has-success',
29822     iconTooltip : 'This field is required',
29823     indicatorpos : 'left',
29824     
29825     getAutoCreate : function(){
29826         
29827         var cfg = {
29828             tag : this.tag,
29829             cls : 'roo-bootstrap-field-label ' + this.cls,
29830             for : this.target,
29831             cn : [
29832                 {
29833                     tag : 'i',
29834                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29835                     tooltip : this.iconTooltip
29836                 },
29837                 {
29838                     tag : 'span',
29839                     html : this.html
29840                 }
29841             ] 
29842         };
29843         
29844         if(this.indicatorpos == 'right'){
29845             var cfg = {
29846                 tag : this.tag,
29847                 cls : 'roo-bootstrap-field-label ' + this.cls,
29848                 for : this.target,
29849                 cn : [
29850                     {
29851                         tag : 'span',
29852                         html : this.html
29853                     },
29854                     {
29855                         tag : 'i',
29856                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29857                         tooltip : this.iconTooltip
29858                     }
29859                 ] 
29860             };
29861         }
29862         
29863         return cfg;
29864     },
29865     
29866     initEvents: function() 
29867     {
29868         Roo.bootstrap.Element.superclass.initEvents.call(this);
29869         
29870         this.indicator = this.indicatorEl();
29871         
29872         if(this.indicator){
29873             this.indicator.removeClass('visible');
29874             this.indicator.addClass('invisible');
29875         }
29876         
29877         Roo.bootstrap.FieldLabel.register(this);
29878     },
29879     
29880     indicatorEl : function()
29881     {
29882         var indicator = this.el.select('i.roo-required-indicator',true).first();
29883         
29884         if(!indicator){
29885             return false;
29886         }
29887         
29888         return indicator;
29889         
29890     },
29891     
29892     /**
29893      * Mark this field as valid
29894      */
29895     markValid : function()
29896     {
29897         if(this.indicator){
29898             this.indicator.removeClass('visible');
29899             this.indicator.addClass('invisible');
29900         }
29901         
29902         this.el.removeClass(this.invalidClass);
29903         
29904         this.el.addClass(this.validClass);
29905         
29906         this.fireEvent('valid', this);
29907     },
29908     
29909     /**
29910      * Mark this field as invalid
29911      * @param {String} msg The validation message
29912      */
29913     markInvalid : function(msg)
29914     {
29915         if(this.indicator){
29916             this.indicator.removeClass('invisible');
29917             this.indicator.addClass('visible');
29918         }
29919         
29920         this.el.removeClass(this.validClass);
29921         
29922         this.el.addClass(this.invalidClass);
29923         
29924         this.fireEvent('invalid', this, msg);
29925     }
29926     
29927    
29928 });
29929
29930 Roo.apply(Roo.bootstrap.FieldLabel, {
29931     
29932     groups: {},
29933     
29934      /**
29935     * register a FieldLabel Group
29936     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29937     */
29938     register : function(label)
29939     {
29940         if(this.groups.hasOwnProperty(label.target)){
29941             return;
29942         }
29943      
29944         this.groups[label.target] = label;
29945         
29946     },
29947     /**
29948     * fetch a FieldLabel Group based on the target
29949     * @param {string} target
29950     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29951     */
29952     get: function(target) {
29953         if (typeof(this.groups[target]) == 'undefined') {
29954             return false;
29955         }
29956         
29957         return this.groups[target] ;
29958     }
29959 });
29960
29961  
29962
29963  /*
29964  * - LGPL
29965  *
29966  * page DateSplitField.
29967  * 
29968  */
29969
29970
29971 /**
29972  * @class Roo.bootstrap.DateSplitField
29973  * @extends Roo.bootstrap.Component
29974  * Bootstrap DateSplitField class
29975  * @cfg {string} fieldLabel - the label associated
29976  * @cfg {Number} labelWidth set the width of label (0-12)
29977  * @cfg {String} labelAlign (top|left)
29978  * @cfg {Boolean} dayAllowBlank (true|false) default false
29979  * @cfg {Boolean} monthAllowBlank (true|false) default false
29980  * @cfg {Boolean} yearAllowBlank (true|false) default false
29981  * @cfg {string} dayPlaceholder 
29982  * @cfg {string} monthPlaceholder
29983  * @cfg {string} yearPlaceholder
29984  * @cfg {string} dayFormat default 'd'
29985  * @cfg {string} monthFormat default 'm'
29986  * @cfg {string} yearFormat default 'Y'
29987  * @cfg {Number} labellg set the width of label (1-12)
29988  * @cfg {Number} labelmd set the width of label (1-12)
29989  * @cfg {Number} labelsm set the width of label (1-12)
29990  * @cfg {Number} labelxs set the width of label (1-12)
29991
29992  *     
29993  * @constructor
29994  * Create a new DateSplitField
29995  * @param {Object} config The config object
29996  */
29997
29998 Roo.bootstrap.DateSplitField = function(config){
29999     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30000     
30001     this.addEvents({
30002         // raw events
30003          /**
30004          * @event years
30005          * getting the data of years
30006          * @param {Roo.bootstrap.DateSplitField} this
30007          * @param {Object} years
30008          */
30009         "years" : true,
30010         /**
30011          * @event days
30012          * getting the data of days
30013          * @param {Roo.bootstrap.DateSplitField} this
30014          * @param {Object} days
30015          */
30016         "days" : true,
30017         /**
30018          * @event invalid
30019          * Fires after the field has been marked as invalid.
30020          * @param {Roo.form.Field} this
30021          * @param {String} msg The validation message
30022          */
30023         invalid : true,
30024        /**
30025          * @event valid
30026          * Fires after the field has been validated with no errors.
30027          * @param {Roo.form.Field} this
30028          */
30029         valid : true
30030     });
30031 };
30032
30033 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30034     
30035     fieldLabel : '',
30036     labelAlign : 'top',
30037     labelWidth : 3,
30038     dayAllowBlank : false,
30039     monthAllowBlank : false,
30040     yearAllowBlank : false,
30041     dayPlaceholder : '',
30042     monthPlaceholder : '',
30043     yearPlaceholder : '',
30044     dayFormat : 'd',
30045     monthFormat : 'm',
30046     yearFormat : 'Y',
30047     isFormField : true,
30048     labellg : 0,
30049     labelmd : 0,
30050     labelsm : 0,
30051     labelxs : 0,
30052     
30053     getAutoCreate : function()
30054     {
30055         var cfg = {
30056             tag : 'div',
30057             cls : 'row roo-date-split-field-group',
30058             cn : [
30059                 {
30060                     tag : 'input',
30061                     type : 'hidden',
30062                     cls : 'form-hidden-field roo-date-split-field-group-value',
30063                     name : this.name
30064                 }
30065             ]
30066         };
30067         
30068         var labelCls = 'col-md-12';
30069         var contentCls = 'col-md-4';
30070         
30071         if(this.fieldLabel){
30072             
30073             var label = {
30074                 tag : 'div',
30075                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30076                 cn : [
30077                     {
30078                         tag : 'label',
30079                         html : this.fieldLabel
30080                     }
30081                 ]
30082             };
30083             
30084             if(this.labelAlign == 'left'){
30085             
30086                 if(this.labelWidth > 12){
30087                     label.style = "width: " + this.labelWidth + 'px';
30088                 }
30089
30090                 if(this.labelWidth < 13 && this.labelmd == 0){
30091                     this.labelmd = this.labelWidth;
30092                 }
30093
30094                 if(this.labellg > 0){
30095                     labelCls = ' col-lg-' + this.labellg;
30096                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30097                 }
30098
30099                 if(this.labelmd > 0){
30100                     labelCls = ' col-md-' + this.labelmd;
30101                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30102                 }
30103
30104                 if(this.labelsm > 0){
30105                     labelCls = ' col-sm-' + this.labelsm;
30106                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30107                 }
30108
30109                 if(this.labelxs > 0){
30110                     labelCls = ' col-xs-' + this.labelxs;
30111                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30112                 }
30113             }
30114             
30115             label.cls += ' ' + labelCls;
30116             
30117             cfg.cn.push(label);
30118         }
30119         
30120         Roo.each(['day', 'month', 'year'], function(t){
30121             cfg.cn.push({
30122                 tag : 'div',
30123                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30124             });
30125         }, this);
30126         
30127         return cfg;
30128     },
30129     
30130     inputEl: function ()
30131     {
30132         return this.el.select('.roo-date-split-field-group-value', true).first();
30133     },
30134     
30135     onRender : function(ct, position) 
30136     {
30137         var _this = this;
30138         
30139         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30140         
30141         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30142         
30143         this.dayField = new Roo.bootstrap.ComboBox({
30144             allowBlank : this.dayAllowBlank,
30145             alwaysQuery : true,
30146             displayField : 'value',
30147             editable : false,
30148             fieldLabel : '',
30149             forceSelection : true,
30150             mode : 'local',
30151             placeholder : this.dayPlaceholder,
30152             selectOnFocus : true,
30153             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30154             triggerAction : 'all',
30155             typeAhead : true,
30156             valueField : 'value',
30157             store : new Roo.data.SimpleStore({
30158                 data : (function() {    
30159                     var days = [];
30160                     _this.fireEvent('days', _this, days);
30161                     return days;
30162                 })(),
30163                 fields : [ 'value' ]
30164             }),
30165             listeners : {
30166                 select : function (_self, record, index)
30167                 {
30168                     _this.setValue(_this.getValue());
30169                 }
30170             }
30171         });
30172
30173         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30174         
30175         this.monthField = new Roo.bootstrap.MonthField({
30176             after : '<i class=\"fa fa-calendar\"></i>',
30177             allowBlank : this.monthAllowBlank,
30178             placeholder : this.monthPlaceholder,
30179             readOnly : true,
30180             listeners : {
30181                 render : function (_self)
30182                 {
30183                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30184                         e.preventDefault();
30185                         _self.focus();
30186                     });
30187                 },
30188                 select : function (_self, oldvalue, newvalue)
30189                 {
30190                     _this.setValue(_this.getValue());
30191                 }
30192             }
30193         });
30194         
30195         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30196         
30197         this.yearField = new Roo.bootstrap.ComboBox({
30198             allowBlank : this.yearAllowBlank,
30199             alwaysQuery : true,
30200             displayField : 'value',
30201             editable : false,
30202             fieldLabel : '',
30203             forceSelection : true,
30204             mode : 'local',
30205             placeholder : this.yearPlaceholder,
30206             selectOnFocus : true,
30207             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30208             triggerAction : 'all',
30209             typeAhead : true,
30210             valueField : 'value',
30211             store : new Roo.data.SimpleStore({
30212                 data : (function() {
30213                     var years = [];
30214                     _this.fireEvent('years', _this, years);
30215                     return years;
30216                 })(),
30217                 fields : [ 'value' ]
30218             }),
30219             listeners : {
30220                 select : function (_self, record, index)
30221                 {
30222                     _this.setValue(_this.getValue());
30223                 }
30224             }
30225         });
30226
30227         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30228     },
30229     
30230     setValue : function(v, format)
30231     {
30232         this.inputEl.dom.value = v;
30233         
30234         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30235         
30236         var d = Date.parseDate(v, f);
30237         
30238         if(!d){
30239             this.validate();
30240             return;
30241         }
30242         
30243         this.setDay(d.format(this.dayFormat));
30244         this.setMonth(d.format(this.monthFormat));
30245         this.setYear(d.format(this.yearFormat));
30246         
30247         this.validate();
30248         
30249         return;
30250     },
30251     
30252     setDay : function(v)
30253     {
30254         this.dayField.setValue(v);
30255         this.inputEl.dom.value = this.getValue();
30256         this.validate();
30257         return;
30258     },
30259     
30260     setMonth : function(v)
30261     {
30262         this.monthField.setValue(v, true);
30263         this.inputEl.dom.value = this.getValue();
30264         this.validate();
30265         return;
30266     },
30267     
30268     setYear : function(v)
30269     {
30270         this.yearField.setValue(v);
30271         this.inputEl.dom.value = this.getValue();
30272         this.validate();
30273         return;
30274     },
30275     
30276     getDay : function()
30277     {
30278         return this.dayField.getValue();
30279     },
30280     
30281     getMonth : function()
30282     {
30283         return this.monthField.getValue();
30284     },
30285     
30286     getYear : function()
30287     {
30288         return this.yearField.getValue();
30289     },
30290     
30291     getValue : function()
30292     {
30293         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30294         
30295         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30296         
30297         return date;
30298     },
30299     
30300     reset : function()
30301     {
30302         this.setDay('');
30303         this.setMonth('');
30304         this.setYear('');
30305         this.inputEl.dom.value = '';
30306         this.validate();
30307         return;
30308     },
30309     
30310     validate : function()
30311     {
30312         var d = this.dayField.validate();
30313         var m = this.monthField.validate();
30314         var y = this.yearField.validate();
30315         
30316         var valid = true;
30317         
30318         if(
30319                 (!this.dayAllowBlank && !d) ||
30320                 (!this.monthAllowBlank && !m) ||
30321                 (!this.yearAllowBlank && !y)
30322         ){
30323             valid = false;
30324         }
30325         
30326         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30327             return valid;
30328         }
30329         
30330         if(valid){
30331             this.markValid();
30332             return valid;
30333         }
30334         
30335         this.markInvalid();
30336         
30337         return valid;
30338     },
30339     
30340     markValid : function()
30341     {
30342         
30343         var label = this.el.select('label', true).first();
30344         var icon = this.el.select('i.fa-star', true).first();
30345
30346         if(label && icon){
30347             icon.remove();
30348         }
30349         
30350         this.fireEvent('valid', this);
30351     },
30352     
30353      /**
30354      * Mark this field as invalid
30355      * @param {String} msg The validation message
30356      */
30357     markInvalid : function(msg)
30358     {
30359         
30360         var label = this.el.select('label', true).first();
30361         var icon = this.el.select('i.fa-star', true).first();
30362
30363         if(label && !icon){
30364             this.el.select('.roo-date-split-field-label', true).createChild({
30365                 tag : 'i',
30366                 cls : 'text-danger fa fa-lg fa-star',
30367                 tooltip : 'This field is required',
30368                 style : 'margin-right:5px;'
30369             }, label, true);
30370         }
30371         
30372         this.fireEvent('invalid', this, msg);
30373     },
30374     
30375     clearInvalid : function()
30376     {
30377         var label = this.el.select('label', true).first();
30378         var icon = this.el.select('i.fa-star', true).first();
30379
30380         if(label && icon){
30381             icon.remove();
30382         }
30383         
30384         this.fireEvent('valid', this);
30385     },
30386     
30387     getName: function()
30388     {
30389         return this.name;
30390     }
30391     
30392 });
30393
30394  /**
30395  *
30396  * This is based on 
30397  * http://masonry.desandro.com
30398  *
30399  * The idea is to render all the bricks based on vertical width...
30400  *
30401  * The original code extends 'outlayer' - we might need to use that....
30402  * 
30403  */
30404
30405
30406 /**
30407  * @class Roo.bootstrap.LayoutMasonry
30408  * @extends Roo.bootstrap.Component
30409  * Bootstrap Layout Masonry class
30410  * 
30411  * @constructor
30412  * Create a new Element
30413  * @param {Object} config The config object
30414  */
30415
30416 Roo.bootstrap.LayoutMasonry = function(config){
30417     
30418     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30419     
30420     this.bricks = [];
30421     
30422     Roo.bootstrap.LayoutMasonry.register(this);
30423     
30424     this.addEvents({
30425         // raw events
30426         /**
30427          * @event layout
30428          * Fire after layout the items
30429          * @param {Roo.bootstrap.LayoutMasonry} this
30430          * @param {Roo.EventObject} e
30431          */
30432         "layout" : true
30433     });
30434     
30435 };
30436
30437 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30438     
30439     /**
30440      * @cfg {Boolean} isLayoutInstant = no animation?
30441      */   
30442     isLayoutInstant : false, // needed?
30443    
30444     /**
30445      * @cfg {Number} boxWidth  width of the columns
30446      */   
30447     boxWidth : 450,
30448     
30449       /**
30450      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30451      */   
30452     boxHeight : 0,
30453     
30454     /**
30455      * @cfg {Number} padWidth padding below box..
30456      */   
30457     padWidth : 10, 
30458     
30459     /**
30460      * @cfg {Number} gutter gutter width..
30461      */   
30462     gutter : 10,
30463     
30464      /**
30465      * @cfg {Number} maxCols maximum number of columns
30466      */   
30467     
30468     maxCols: 0,
30469     
30470     /**
30471      * @cfg {Boolean} isAutoInitial defalut true
30472      */   
30473     isAutoInitial : true, 
30474     
30475     containerWidth: 0,
30476     
30477     /**
30478      * @cfg {Boolean} isHorizontal defalut false
30479      */   
30480     isHorizontal : false, 
30481
30482     currentSize : null,
30483     
30484     tag: 'div',
30485     
30486     cls: '',
30487     
30488     bricks: null, //CompositeElement
30489     
30490     cols : 1,
30491     
30492     _isLayoutInited : false,
30493     
30494 //    isAlternative : false, // only use for vertical layout...
30495     
30496     /**
30497      * @cfg {Number} alternativePadWidth padding below box..
30498      */   
30499     alternativePadWidth : 50,
30500     
30501     selectedBrick : [],
30502     
30503     getAutoCreate : function(){
30504         
30505         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30506         
30507         var cfg = {
30508             tag: this.tag,
30509             cls: 'blog-masonary-wrapper ' + this.cls,
30510             cn : {
30511                 cls : 'mas-boxes masonary'
30512             }
30513         };
30514         
30515         return cfg;
30516     },
30517     
30518     getChildContainer: function( )
30519     {
30520         if (this.boxesEl) {
30521             return this.boxesEl;
30522         }
30523         
30524         this.boxesEl = this.el.select('.mas-boxes').first();
30525         
30526         return this.boxesEl;
30527     },
30528     
30529     
30530     initEvents : function()
30531     {
30532         var _this = this;
30533         
30534         if(this.isAutoInitial){
30535             Roo.log('hook children rendered');
30536             this.on('childrenrendered', function() {
30537                 Roo.log('children rendered');
30538                 _this.initial();
30539             } ,this);
30540         }
30541     },
30542     
30543     initial : function()
30544     {
30545         this.selectedBrick = [];
30546         
30547         this.currentSize = this.el.getBox(true);
30548         
30549         Roo.EventManager.onWindowResize(this.resize, this); 
30550
30551         if(!this.isAutoInitial){
30552             this.layout();
30553             return;
30554         }
30555         
30556         this.layout();
30557         
30558         return;
30559         //this.layout.defer(500,this);
30560         
30561     },
30562     
30563     resize : function()
30564     {
30565         var cs = this.el.getBox(true);
30566         
30567         if (
30568                 this.currentSize.width == cs.width && 
30569                 this.currentSize.x == cs.x && 
30570                 this.currentSize.height == cs.height && 
30571                 this.currentSize.y == cs.y 
30572         ) {
30573             Roo.log("no change in with or X or Y");
30574             return;
30575         }
30576         
30577         this.currentSize = cs;
30578         
30579         this.layout();
30580         
30581     },
30582     
30583     layout : function()
30584     {   
30585         this._resetLayout();
30586         
30587         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30588         
30589         this.layoutItems( isInstant );
30590       
30591         this._isLayoutInited = true;
30592         
30593         this.fireEvent('layout', this);
30594         
30595     },
30596     
30597     _resetLayout : function()
30598     {
30599         if(this.isHorizontal){
30600             this.horizontalMeasureColumns();
30601             return;
30602         }
30603         
30604         this.verticalMeasureColumns();
30605         
30606     },
30607     
30608     verticalMeasureColumns : function()
30609     {
30610         this.getContainerWidth();
30611         
30612 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30613 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30614 //            return;
30615 //        }
30616         
30617         var boxWidth = this.boxWidth + this.padWidth;
30618         
30619         if(this.containerWidth < this.boxWidth){
30620             boxWidth = this.containerWidth
30621         }
30622         
30623         var containerWidth = this.containerWidth;
30624         
30625         var cols = Math.floor(containerWidth / boxWidth);
30626         
30627         this.cols = Math.max( cols, 1 );
30628         
30629         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30630         
30631         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30632         
30633         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30634         
30635         this.colWidth = boxWidth + avail - this.padWidth;
30636         
30637         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30638         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30639     },
30640     
30641     horizontalMeasureColumns : function()
30642     {
30643         this.getContainerWidth();
30644         
30645         var boxWidth = this.boxWidth;
30646         
30647         if(this.containerWidth < boxWidth){
30648             boxWidth = this.containerWidth;
30649         }
30650         
30651         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30652         
30653         this.el.setHeight(boxWidth);
30654         
30655     },
30656     
30657     getContainerWidth : function()
30658     {
30659         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30660     },
30661     
30662     layoutItems : function( isInstant )
30663     {
30664         Roo.log(this.bricks);
30665         
30666         var items = Roo.apply([], this.bricks);
30667         
30668         if(this.isHorizontal){
30669             this._horizontalLayoutItems( items , isInstant );
30670             return;
30671         }
30672         
30673 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30674 //            this._verticalAlternativeLayoutItems( items , isInstant );
30675 //            return;
30676 //        }
30677         
30678         this._verticalLayoutItems( items , isInstant );
30679         
30680     },
30681     
30682     _verticalLayoutItems : function ( items , isInstant)
30683     {
30684         if ( !items || !items.length ) {
30685             return;
30686         }
30687         
30688         var standard = [
30689             ['xs', 'xs', 'xs', 'tall'],
30690             ['xs', 'xs', 'tall'],
30691             ['xs', 'xs', 'sm'],
30692             ['xs', 'xs', 'xs'],
30693             ['xs', 'tall'],
30694             ['xs', 'sm'],
30695             ['xs', 'xs'],
30696             ['xs'],
30697             
30698             ['sm', 'xs', 'xs'],
30699             ['sm', 'xs'],
30700             ['sm'],
30701             
30702             ['tall', 'xs', 'xs', 'xs'],
30703             ['tall', 'xs', 'xs'],
30704             ['tall', 'xs'],
30705             ['tall']
30706             
30707         ];
30708         
30709         var queue = [];
30710         
30711         var boxes = [];
30712         
30713         var box = [];
30714         
30715         Roo.each(items, function(item, k){
30716             
30717             switch (item.size) {
30718                 // these layouts take up a full box,
30719                 case 'md' :
30720                 case 'md-left' :
30721                 case 'md-right' :
30722                 case 'wide' :
30723                     
30724                     if(box.length){
30725                         boxes.push(box);
30726                         box = [];
30727                     }
30728                     
30729                     boxes.push([item]);
30730                     
30731                     break;
30732                     
30733                 case 'xs' :
30734                 case 'sm' :
30735                 case 'tall' :
30736                     
30737                     box.push(item);
30738                     
30739                     break;
30740                 default :
30741                     break;
30742                     
30743             }
30744             
30745         }, this);
30746         
30747         if(box.length){
30748             boxes.push(box);
30749             box = [];
30750         }
30751         
30752         var filterPattern = function(box, length)
30753         {
30754             if(!box.length){
30755                 return;
30756             }
30757             
30758             var match = false;
30759             
30760             var pattern = box.slice(0, length);
30761             
30762             var format = [];
30763             
30764             Roo.each(pattern, function(i){
30765                 format.push(i.size);
30766             }, this);
30767             
30768             Roo.each(standard, function(s){
30769                 
30770                 if(String(s) != String(format)){
30771                     return;
30772                 }
30773                 
30774                 match = true;
30775                 return false;
30776                 
30777             }, this);
30778             
30779             if(!match && length == 1){
30780                 return;
30781             }
30782             
30783             if(!match){
30784                 filterPattern(box, length - 1);
30785                 return;
30786             }
30787                 
30788             queue.push(pattern);
30789
30790             box = box.slice(length, box.length);
30791
30792             filterPattern(box, 4);
30793
30794             return;
30795             
30796         }
30797         
30798         Roo.each(boxes, function(box, k){
30799             
30800             if(!box.length){
30801                 return;
30802             }
30803             
30804             if(box.length == 1){
30805                 queue.push(box);
30806                 return;
30807             }
30808             
30809             filterPattern(box, 4);
30810             
30811         }, this);
30812         
30813         this._processVerticalLayoutQueue( queue, isInstant );
30814         
30815     },
30816     
30817 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30818 //    {
30819 //        if ( !items || !items.length ) {
30820 //            return;
30821 //        }
30822 //
30823 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30824 //        
30825 //    },
30826     
30827     _horizontalLayoutItems : function ( items , isInstant)
30828     {
30829         if ( !items || !items.length || items.length < 3) {
30830             return;
30831         }
30832         
30833         items.reverse();
30834         
30835         var eItems = items.slice(0, 3);
30836         
30837         items = items.slice(3, items.length);
30838         
30839         var standard = [
30840             ['xs', 'xs', 'xs', 'wide'],
30841             ['xs', 'xs', 'wide'],
30842             ['xs', 'xs', 'sm'],
30843             ['xs', 'xs', 'xs'],
30844             ['xs', 'wide'],
30845             ['xs', 'sm'],
30846             ['xs', 'xs'],
30847             ['xs'],
30848             
30849             ['sm', 'xs', 'xs'],
30850             ['sm', 'xs'],
30851             ['sm'],
30852             
30853             ['wide', 'xs', 'xs', 'xs'],
30854             ['wide', 'xs', 'xs'],
30855             ['wide', 'xs'],
30856             ['wide'],
30857             
30858             ['wide-thin']
30859         ];
30860         
30861         var queue = [];
30862         
30863         var boxes = [];
30864         
30865         var box = [];
30866         
30867         Roo.each(items, function(item, k){
30868             
30869             switch (item.size) {
30870                 case 'md' :
30871                 case 'md-left' :
30872                 case 'md-right' :
30873                 case 'tall' :
30874                     
30875                     if(box.length){
30876                         boxes.push(box);
30877                         box = [];
30878                     }
30879                     
30880                     boxes.push([item]);
30881                     
30882                     break;
30883                     
30884                 case 'xs' :
30885                 case 'sm' :
30886                 case 'wide' :
30887                 case 'wide-thin' :
30888                     
30889                     box.push(item);
30890                     
30891                     break;
30892                 default :
30893                     break;
30894                     
30895             }
30896             
30897         }, this);
30898         
30899         if(box.length){
30900             boxes.push(box);
30901             box = [];
30902         }
30903         
30904         var filterPattern = function(box, length)
30905         {
30906             if(!box.length){
30907                 return;
30908             }
30909             
30910             var match = false;
30911             
30912             var pattern = box.slice(0, length);
30913             
30914             var format = [];
30915             
30916             Roo.each(pattern, function(i){
30917                 format.push(i.size);
30918             }, this);
30919             
30920             Roo.each(standard, function(s){
30921                 
30922                 if(String(s) != String(format)){
30923                     return;
30924                 }
30925                 
30926                 match = true;
30927                 return false;
30928                 
30929             }, this);
30930             
30931             if(!match && length == 1){
30932                 return;
30933             }
30934             
30935             if(!match){
30936                 filterPattern(box, length - 1);
30937                 return;
30938             }
30939                 
30940             queue.push(pattern);
30941
30942             box = box.slice(length, box.length);
30943
30944             filterPattern(box, 4);
30945
30946             return;
30947             
30948         }
30949         
30950         Roo.each(boxes, function(box, k){
30951             
30952             if(!box.length){
30953                 return;
30954             }
30955             
30956             if(box.length == 1){
30957                 queue.push(box);
30958                 return;
30959             }
30960             
30961             filterPattern(box, 4);
30962             
30963         }, this);
30964         
30965         
30966         var prune = [];
30967         
30968         var pos = this.el.getBox(true);
30969         
30970         var minX = pos.x;
30971         
30972         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30973         
30974         var hit_end = false;
30975         
30976         Roo.each(queue, function(box){
30977             
30978             if(hit_end){
30979                 
30980                 Roo.each(box, function(b){
30981                 
30982                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30983                     b.el.hide();
30984
30985                 }, this);
30986
30987                 return;
30988             }
30989             
30990             var mx = 0;
30991             
30992             Roo.each(box, function(b){
30993                 
30994                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30995                 b.el.show();
30996
30997                 mx = Math.max(mx, b.x);
30998                 
30999             }, this);
31000             
31001             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31002             
31003             if(maxX < minX){
31004                 
31005                 Roo.each(box, function(b){
31006                 
31007                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31008                     b.el.hide();
31009                     
31010                 }, this);
31011                 
31012                 hit_end = true;
31013                 
31014                 return;
31015             }
31016             
31017             prune.push(box);
31018             
31019         }, this);
31020         
31021         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31022     },
31023     
31024     /** Sets position of item in DOM
31025     * @param {Element} item
31026     * @param {Number} x - horizontal position
31027     * @param {Number} y - vertical position
31028     * @param {Boolean} isInstant - disables transitions
31029     */
31030     _processVerticalLayoutQueue : function( queue, isInstant )
31031     {
31032         var pos = this.el.getBox(true);
31033         var x = pos.x;
31034         var y = pos.y;
31035         var maxY = [];
31036         
31037         for (var i = 0; i < this.cols; i++){
31038             maxY[i] = pos.y;
31039         }
31040         
31041         Roo.each(queue, function(box, k){
31042             
31043             var col = k % this.cols;
31044             
31045             Roo.each(box, function(b,kk){
31046                 
31047                 b.el.position('absolute');
31048                 
31049                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31050                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31051                 
31052                 if(b.size == 'md-left' || b.size == 'md-right'){
31053                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31054                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31055                 }
31056                 
31057                 b.el.setWidth(width);
31058                 b.el.setHeight(height);
31059                 // iframe?
31060                 b.el.select('iframe',true).setSize(width,height);
31061                 
31062             }, this);
31063             
31064             for (var i = 0; i < this.cols; i++){
31065                 
31066                 if(maxY[i] < maxY[col]){
31067                     col = i;
31068                     continue;
31069                 }
31070                 
31071                 col = Math.min(col, i);
31072                 
31073             }
31074             
31075             x = pos.x + col * (this.colWidth + this.padWidth);
31076             
31077             y = maxY[col];
31078             
31079             var positions = [];
31080             
31081             switch (box.length){
31082                 case 1 :
31083                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31084                     break;
31085                 case 2 :
31086                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31087                     break;
31088                 case 3 :
31089                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31090                     break;
31091                 case 4 :
31092                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31093                     break;
31094                 default :
31095                     break;
31096             }
31097             
31098             Roo.each(box, function(b,kk){
31099                 
31100                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31101                 
31102                 var sz = b.el.getSize();
31103                 
31104                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31105                 
31106             }, this);
31107             
31108         }, this);
31109         
31110         var mY = 0;
31111         
31112         for (var i = 0; i < this.cols; i++){
31113             mY = Math.max(mY, maxY[i]);
31114         }
31115         
31116         this.el.setHeight(mY - pos.y);
31117         
31118     },
31119     
31120 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31121 //    {
31122 //        var pos = this.el.getBox(true);
31123 //        var x = pos.x;
31124 //        var y = pos.y;
31125 //        var maxX = pos.right;
31126 //        
31127 //        var maxHeight = 0;
31128 //        
31129 //        Roo.each(items, function(item, k){
31130 //            
31131 //            var c = k % 2;
31132 //            
31133 //            item.el.position('absolute');
31134 //                
31135 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31136 //
31137 //            item.el.setWidth(width);
31138 //
31139 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31140 //
31141 //            item.el.setHeight(height);
31142 //            
31143 //            if(c == 0){
31144 //                item.el.setXY([x, y], isInstant ? false : true);
31145 //            } else {
31146 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31147 //            }
31148 //            
31149 //            y = y + height + this.alternativePadWidth;
31150 //            
31151 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31152 //            
31153 //        }, this);
31154 //        
31155 //        this.el.setHeight(maxHeight);
31156 //        
31157 //    },
31158     
31159     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31160     {
31161         var pos = this.el.getBox(true);
31162         
31163         var minX = pos.x;
31164         var minY = pos.y;
31165         
31166         var maxX = pos.right;
31167         
31168         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31169         
31170         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31171         
31172         Roo.each(queue, function(box, k){
31173             
31174             Roo.each(box, function(b, kk){
31175                 
31176                 b.el.position('absolute');
31177                 
31178                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31179                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31180                 
31181                 if(b.size == 'md-left' || b.size == 'md-right'){
31182                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31183                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31184                 }
31185                 
31186                 b.el.setWidth(width);
31187                 b.el.setHeight(height);
31188                 
31189             }, this);
31190             
31191             if(!box.length){
31192                 return;
31193             }
31194             
31195             var positions = [];
31196             
31197             switch (box.length){
31198                 case 1 :
31199                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31200                     break;
31201                 case 2 :
31202                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31203                     break;
31204                 case 3 :
31205                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31206                     break;
31207                 case 4 :
31208                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31209                     break;
31210                 default :
31211                     break;
31212             }
31213             
31214             Roo.each(box, function(b,kk){
31215                 
31216                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31217                 
31218                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31219                 
31220             }, this);
31221             
31222         }, this);
31223         
31224     },
31225     
31226     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31227     {
31228         Roo.each(eItems, function(b,k){
31229             
31230             b.size = (k == 0) ? 'sm' : 'xs';
31231             b.x = (k == 0) ? 2 : 1;
31232             b.y = (k == 0) ? 2 : 1;
31233             
31234             b.el.position('absolute');
31235             
31236             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31237                 
31238             b.el.setWidth(width);
31239             
31240             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31241             
31242             b.el.setHeight(height);
31243             
31244         }, this);
31245
31246         var positions = [];
31247         
31248         positions.push({
31249             x : maxX - this.unitWidth * 2 - this.gutter,
31250             y : minY
31251         });
31252         
31253         positions.push({
31254             x : maxX - this.unitWidth,
31255             y : minY + (this.unitWidth + this.gutter) * 2
31256         });
31257         
31258         positions.push({
31259             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31260             y : minY
31261         });
31262         
31263         Roo.each(eItems, function(b,k){
31264             
31265             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31266
31267         }, this);
31268         
31269     },
31270     
31271     getVerticalOneBoxColPositions : function(x, y, box)
31272     {
31273         var pos = [];
31274         
31275         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31276         
31277         if(box[0].size == 'md-left'){
31278             rand = 0;
31279         }
31280         
31281         if(box[0].size == 'md-right'){
31282             rand = 1;
31283         }
31284         
31285         pos.push({
31286             x : x + (this.unitWidth + this.gutter) * rand,
31287             y : y
31288         });
31289         
31290         return pos;
31291     },
31292     
31293     getVerticalTwoBoxColPositions : function(x, y, box)
31294     {
31295         var pos = [];
31296         
31297         if(box[0].size == 'xs'){
31298             
31299             pos.push({
31300                 x : x,
31301                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31302             });
31303
31304             pos.push({
31305                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31306                 y : y
31307             });
31308             
31309             return pos;
31310             
31311         }
31312         
31313         pos.push({
31314             x : x,
31315             y : y
31316         });
31317
31318         pos.push({
31319             x : x + (this.unitWidth + this.gutter) * 2,
31320             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31321         });
31322         
31323         return pos;
31324         
31325     },
31326     
31327     getVerticalThreeBoxColPositions : function(x, y, box)
31328     {
31329         var pos = [];
31330         
31331         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31332             
31333             pos.push({
31334                 x : x,
31335                 y : y
31336             });
31337
31338             pos.push({
31339                 x : x + (this.unitWidth + this.gutter) * 1,
31340                 y : y
31341             });
31342             
31343             pos.push({
31344                 x : x + (this.unitWidth + this.gutter) * 2,
31345                 y : y
31346             });
31347             
31348             return pos;
31349             
31350         }
31351         
31352         if(box[0].size == 'xs' && box[1].size == 'xs'){
31353             
31354             pos.push({
31355                 x : x,
31356                 y : y
31357             });
31358
31359             pos.push({
31360                 x : x,
31361                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31362             });
31363             
31364             pos.push({
31365                 x : x + (this.unitWidth + this.gutter) * 1,
31366                 y : y
31367             });
31368             
31369             return pos;
31370             
31371         }
31372         
31373         pos.push({
31374             x : x,
31375             y : y
31376         });
31377
31378         pos.push({
31379             x : x + (this.unitWidth + this.gutter) * 2,
31380             y : y
31381         });
31382
31383         pos.push({
31384             x : x + (this.unitWidth + this.gutter) * 2,
31385             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31386         });
31387             
31388         return pos;
31389         
31390     },
31391     
31392     getVerticalFourBoxColPositions : function(x, y, box)
31393     {
31394         var pos = [];
31395         
31396         if(box[0].size == 'xs'){
31397             
31398             pos.push({
31399                 x : x,
31400                 y : y
31401             });
31402
31403             pos.push({
31404                 x : x,
31405                 y : y + (this.unitHeight + this.gutter) * 1
31406             });
31407             
31408             pos.push({
31409                 x : x,
31410                 y : y + (this.unitHeight + this.gutter) * 2
31411             });
31412             
31413             pos.push({
31414                 x : x + (this.unitWidth + this.gutter) * 1,
31415                 y : y
31416             });
31417             
31418             return pos;
31419             
31420         }
31421         
31422         pos.push({
31423             x : x,
31424             y : y
31425         });
31426
31427         pos.push({
31428             x : x + (this.unitWidth + this.gutter) * 2,
31429             y : y
31430         });
31431
31432         pos.push({
31433             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31434             y : y + (this.unitHeight + this.gutter) * 1
31435         });
31436
31437         pos.push({
31438             x : x + (this.unitWidth + this.gutter) * 2,
31439             y : y + (this.unitWidth + this.gutter) * 2
31440         });
31441
31442         return pos;
31443         
31444     },
31445     
31446     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31447     {
31448         var pos = [];
31449         
31450         if(box[0].size == 'md-left'){
31451             pos.push({
31452                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31453                 y : minY
31454             });
31455             
31456             return pos;
31457         }
31458         
31459         if(box[0].size == 'md-right'){
31460             pos.push({
31461                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31462                 y : minY + (this.unitWidth + this.gutter) * 1
31463             });
31464             
31465             return pos;
31466         }
31467         
31468         var rand = Math.floor(Math.random() * (4 - box[0].y));
31469         
31470         pos.push({
31471             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31472             y : minY + (this.unitWidth + this.gutter) * rand
31473         });
31474         
31475         return pos;
31476         
31477     },
31478     
31479     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31480     {
31481         var pos = [];
31482         
31483         if(box[0].size == 'xs'){
31484             
31485             pos.push({
31486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31487                 y : minY
31488             });
31489
31490             pos.push({
31491                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31492                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31493             });
31494             
31495             return pos;
31496             
31497         }
31498         
31499         pos.push({
31500             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31501             y : minY
31502         });
31503
31504         pos.push({
31505             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31506             y : minY + (this.unitWidth + this.gutter) * 2
31507         });
31508         
31509         return pos;
31510         
31511     },
31512     
31513     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31514     {
31515         var pos = [];
31516         
31517         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31518             
31519             pos.push({
31520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31521                 y : minY
31522             });
31523
31524             pos.push({
31525                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31526                 y : minY + (this.unitWidth + this.gutter) * 1
31527             });
31528             
31529             pos.push({
31530                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31531                 y : minY + (this.unitWidth + this.gutter) * 2
31532             });
31533             
31534             return pos;
31535             
31536         }
31537         
31538         if(box[0].size == 'xs' && box[1].size == 'xs'){
31539             
31540             pos.push({
31541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31542                 y : minY
31543             });
31544
31545             pos.push({
31546                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31547                 y : minY
31548             });
31549             
31550             pos.push({
31551                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31552                 y : minY + (this.unitWidth + this.gutter) * 1
31553             });
31554             
31555             return pos;
31556             
31557         }
31558         
31559         pos.push({
31560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31561             y : minY
31562         });
31563
31564         pos.push({
31565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31566             y : minY + (this.unitWidth + this.gutter) * 2
31567         });
31568
31569         pos.push({
31570             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31571             y : minY + (this.unitWidth + this.gutter) * 2
31572         });
31573             
31574         return pos;
31575         
31576     },
31577     
31578     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31579     {
31580         var pos = [];
31581         
31582         if(box[0].size == 'xs'){
31583             
31584             pos.push({
31585                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31586                 y : minY
31587             });
31588
31589             pos.push({
31590                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31591                 y : minY
31592             });
31593             
31594             pos.push({
31595                 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),
31596                 y : minY
31597             });
31598             
31599             pos.push({
31600                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31601                 y : minY + (this.unitWidth + this.gutter) * 1
31602             });
31603             
31604             return pos;
31605             
31606         }
31607         
31608         pos.push({
31609             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31610             y : minY
31611         });
31612         
31613         pos.push({
31614             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31615             y : minY + (this.unitWidth + this.gutter) * 2
31616         });
31617         
31618         pos.push({
31619             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31620             y : minY + (this.unitWidth + this.gutter) * 2
31621         });
31622         
31623         pos.push({
31624             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),
31625             y : minY + (this.unitWidth + this.gutter) * 2
31626         });
31627
31628         return pos;
31629         
31630     },
31631     
31632     /**
31633     * remove a Masonry Brick
31634     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31635     */
31636     removeBrick : function(brick_id)
31637     {
31638         if (!brick_id) {
31639             return;
31640         }
31641         
31642         for (var i = 0; i<this.bricks.length; i++) {
31643             if (this.bricks[i].id == brick_id) {
31644                 this.bricks.splice(i,1);
31645                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31646                 this.initial();
31647             }
31648         }
31649     },
31650     
31651     /**
31652     * adds a Masonry Brick
31653     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31654     */
31655     addBrick : function(cfg)
31656     {
31657         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31658         //this.register(cn);
31659         cn.parentId = this.id;
31660         cn.onRender(this.el, null);
31661         return cn;
31662     },
31663     
31664     /**
31665     * register a Masonry Brick
31666     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31667     */
31668     
31669     register : function(brick)
31670     {
31671         this.bricks.push(brick);
31672         brick.masonryId = this.id;
31673     },
31674     
31675     /**
31676     * clear all the Masonry Brick
31677     */
31678     clearAll : function()
31679     {
31680         this.bricks = [];
31681         //this.getChildContainer().dom.innerHTML = "";
31682         this.el.dom.innerHTML = '';
31683     },
31684     
31685     getSelected : function()
31686     {
31687         if (!this.selectedBrick) {
31688             return false;
31689         }
31690         
31691         return this.selectedBrick;
31692     }
31693 });
31694
31695 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31696     
31697     groups: {},
31698      /**
31699     * register a Masonry Layout
31700     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31701     */
31702     
31703     register : function(layout)
31704     {
31705         this.groups[layout.id] = layout;
31706     },
31707     /**
31708     * fetch a  Masonry Layout based on the masonry layout ID
31709     * @param {string} the masonry layout to add
31710     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31711     */
31712     
31713     get: function(layout_id) {
31714         if (typeof(this.groups[layout_id]) == 'undefined') {
31715             return false;
31716         }
31717         return this.groups[layout_id] ;
31718     }
31719     
31720     
31721     
31722 });
31723
31724  
31725
31726  /**
31727  *
31728  * This is based on 
31729  * http://masonry.desandro.com
31730  *
31731  * The idea is to render all the bricks based on vertical width...
31732  *
31733  * The original code extends 'outlayer' - we might need to use that....
31734  * 
31735  */
31736
31737
31738 /**
31739  * @class Roo.bootstrap.LayoutMasonryAuto
31740  * @extends Roo.bootstrap.Component
31741  * Bootstrap Layout Masonry class
31742  * 
31743  * @constructor
31744  * Create a new Element
31745  * @param {Object} config The config object
31746  */
31747
31748 Roo.bootstrap.LayoutMasonryAuto = function(config){
31749     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31750 };
31751
31752 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31753     
31754       /**
31755      * @cfg {Boolean} isFitWidth  - resize the width..
31756      */   
31757     isFitWidth : false,  // options..
31758     /**
31759      * @cfg {Boolean} isOriginLeft = left align?
31760      */   
31761     isOriginLeft : true,
31762     /**
31763      * @cfg {Boolean} isOriginTop = top align?
31764      */   
31765     isOriginTop : false,
31766     /**
31767      * @cfg {Boolean} isLayoutInstant = no animation?
31768      */   
31769     isLayoutInstant : false, // needed?
31770     /**
31771      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31772      */   
31773     isResizingContainer : true,
31774     /**
31775      * @cfg {Number} columnWidth  width of the columns 
31776      */   
31777     
31778     columnWidth : 0,
31779     
31780     /**
31781      * @cfg {Number} maxCols maximum number of columns
31782      */   
31783     
31784     maxCols: 0,
31785     /**
31786      * @cfg {Number} padHeight padding below box..
31787      */   
31788     
31789     padHeight : 10, 
31790     
31791     /**
31792      * @cfg {Boolean} isAutoInitial defalut true
31793      */   
31794     
31795     isAutoInitial : true, 
31796     
31797     // private?
31798     gutter : 0,
31799     
31800     containerWidth: 0,
31801     initialColumnWidth : 0,
31802     currentSize : null,
31803     
31804     colYs : null, // array.
31805     maxY : 0,
31806     padWidth: 10,
31807     
31808     
31809     tag: 'div',
31810     cls: '',
31811     bricks: null, //CompositeElement
31812     cols : 0, // array?
31813     // element : null, // wrapped now this.el
31814     _isLayoutInited : null, 
31815     
31816     
31817     getAutoCreate : function(){
31818         
31819         var cfg = {
31820             tag: this.tag,
31821             cls: 'blog-masonary-wrapper ' + this.cls,
31822             cn : {
31823                 cls : 'mas-boxes masonary'
31824             }
31825         };
31826         
31827         return cfg;
31828     },
31829     
31830     getChildContainer: function( )
31831     {
31832         if (this.boxesEl) {
31833             return this.boxesEl;
31834         }
31835         
31836         this.boxesEl = this.el.select('.mas-boxes').first();
31837         
31838         return this.boxesEl;
31839     },
31840     
31841     
31842     initEvents : function()
31843     {
31844         var _this = this;
31845         
31846         if(this.isAutoInitial){
31847             Roo.log('hook children rendered');
31848             this.on('childrenrendered', function() {
31849                 Roo.log('children rendered');
31850                 _this.initial();
31851             } ,this);
31852         }
31853         
31854     },
31855     
31856     initial : function()
31857     {
31858         this.reloadItems();
31859
31860         this.currentSize = this.el.getBox(true);
31861
31862         /// was window resize... - let's see if this works..
31863         Roo.EventManager.onWindowResize(this.resize, this); 
31864
31865         if(!this.isAutoInitial){
31866             this.layout();
31867             return;
31868         }
31869         
31870         this.layout.defer(500,this);
31871     },
31872     
31873     reloadItems: function()
31874     {
31875         this.bricks = this.el.select('.masonry-brick', true);
31876         
31877         this.bricks.each(function(b) {
31878             //Roo.log(b.getSize());
31879             if (!b.attr('originalwidth')) {
31880                 b.attr('originalwidth',  b.getSize().width);
31881             }
31882             
31883         });
31884         
31885         Roo.log(this.bricks.elements.length);
31886     },
31887     
31888     resize : function()
31889     {
31890         Roo.log('resize');
31891         var cs = this.el.getBox(true);
31892         
31893         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31894             Roo.log("no change in with or X");
31895             return;
31896         }
31897         this.currentSize = cs;
31898         this.layout();
31899     },
31900     
31901     layout : function()
31902     {
31903          Roo.log('layout');
31904         this._resetLayout();
31905         //this._manageStamps();
31906       
31907         // don't animate first layout
31908         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31909         this.layoutItems( isInstant );
31910       
31911         // flag for initalized
31912         this._isLayoutInited = true;
31913     },
31914     
31915     layoutItems : function( isInstant )
31916     {
31917         //var items = this._getItemsForLayout( this.items );
31918         // original code supports filtering layout items.. we just ignore it..
31919         
31920         this._layoutItems( this.bricks , isInstant );
31921       
31922         this._postLayout();
31923     },
31924     _layoutItems : function ( items , isInstant)
31925     {
31926        //this.fireEvent( 'layout', this, items );
31927     
31928
31929         if ( !items || !items.elements.length ) {
31930           // no items, emit event with empty array
31931             return;
31932         }
31933
31934         var queue = [];
31935         items.each(function(item) {
31936             Roo.log("layout item");
31937             Roo.log(item);
31938             // get x/y object from method
31939             var position = this._getItemLayoutPosition( item );
31940             // enqueue
31941             position.item = item;
31942             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31943             queue.push( position );
31944         }, this);
31945       
31946         this._processLayoutQueue( queue );
31947     },
31948     /** Sets position of item in DOM
31949     * @param {Element} item
31950     * @param {Number} x - horizontal position
31951     * @param {Number} y - vertical position
31952     * @param {Boolean} isInstant - disables transitions
31953     */
31954     _processLayoutQueue : function( queue )
31955     {
31956         for ( var i=0, len = queue.length; i < len; i++ ) {
31957             var obj = queue[i];
31958             obj.item.position('absolute');
31959             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31960         }
31961     },
31962       
31963     
31964     /**
31965     * Any logic you want to do after each layout,
31966     * i.e. size the container
31967     */
31968     _postLayout : function()
31969     {
31970         this.resizeContainer();
31971     },
31972     
31973     resizeContainer : function()
31974     {
31975         if ( !this.isResizingContainer ) {
31976             return;
31977         }
31978         var size = this._getContainerSize();
31979         if ( size ) {
31980             this.el.setSize(size.width,size.height);
31981             this.boxesEl.setSize(size.width,size.height);
31982         }
31983     },
31984     
31985     
31986     
31987     _resetLayout : function()
31988     {
31989         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31990         this.colWidth = this.el.getWidth();
31991         //this.gutter = this.el.getWidth(); 
31992         
31993         this.measureColumns();
31994
31995         // reset column Y
31996         var i = this.cols;
31997         this.colYs = [];
31998         while (i--) {
31999             this.colYs.push( 0 );
32000         }
32001     
32002         this.maxY = 0;
32003     },
32004
32005     measureColumns : function()
32006     {
32007         this.getContainerWidth();
32008       // if columnWidth is 0, default to outerWidth of first item
32009         if ( !this.columnWidth ) {
32010             var firstItem = this.bricks.first();
32011             Roo.log(firstItem);
32012             this.columnWidth  = this.containerWidth;
32013             if (firstItem && firstItem.attr('originalwidth') ) {
32014                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32015             }
32016             // columnWidth fall back to item of first element
32017             Roo.log("set column width?");
32018                         this.initialColumnWidth = this.columnWidth  ;
32019
32020             // if first elem has no width, default to size of container
32021             
32022         }
32023         
32024         
32025         if (this.initialColumnWidth) {
32026             this.columnWidth = this.initialColumnWidth;
32027         }
32028         
32029         
32030             
32031         // column width is fixed at the top - however if container width get's smaller we should
32032         // reduce it...
32033         
32034         // this bit calcs how man columns..
32035             
32036         var columnWidth = this.columnWidth += this.gutter;
32037       
32038         // calculate columns
32039         var containerWidth = this.containerWidth + this.gutter;
32040         
32041         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32042         // fix rounding errors, typically with gutters
32043         var excess = columnWidth - containerWidth % columnWidth;
32044         
32045         
32046         // if overshoot is less than a pixel, round up, otherwise floor it
32047         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32048         cols = Math[ mathMethod ]( cols );
32049         this.cols = Math.max( cols, 1 );
32050         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32051         
32052          // padding positioning..
32053         var totalColWidth = this.cols * this.columnWidth;
32054         var padavail = this.containerWidth - totalColWidth;
32055         // so for 2 columns - we need 3 'pads'
32056         
32057         var padNeeded = (1+this.cols) * this.padWidth;
32058         
32059         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32060         
32061         this.columnWidth += padExtra
32062         //this.padWidth = Math.floor(padavail /  ( this.cols));
32063         
32064         // adjust colum width so that padding is fixed??
32065         
32066         // we have 3 columns ... total = width * 3
32067         // we have X left over... that should be used by 
32068         
32069         //if (this.expandC) {
32070             
32071         //}
32072         
32073         
32074         
32075     },
32076     
32077     getContainerWidth : function()
32078     {
32079        /* // container is parent if fit width
32080         var container = this.isFitWidth ? this.element.parentNode : this.element;
32081         // check that this.size and size are there
32082         // IE8 triggers resize on body size change, so they might not be
32083         
32084         var size = getSize( container );  //FIXME
32085         this.containerWidth = size && size.innerWidth; //FIXME
32086         */
32087          
32088         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32089         
32090     },
32091     
32092     _getItemLayoutPosition : function( item )  // what is item?
32093     {
32094         // we resize the item to our columnWidth..
32095       
32096         item.setWidth(this.columnWidth);
32097         item.autoBoxAdjust  = false;
32098         
32099         var sz = item.getSize();
32100  
32101         // how many columns does this brick span
32102         var remainder = this.containerWidth % this.columnWidth;
32103         
32104         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32105         // round if off by 1 pixel, otherwise use ceil
32106         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32107         colSpan = Math.min( colSpan, this.cols );
32108         
32109         // normally this should be '1' as we dont' currently allow multi width columns..
32110         
32111         var colGroup = this._getColGroup( colSpan );
32112         // get the minimum Y value from the columns
32113         var minimumY = Math.min.apply( Math, colGroup );
32114         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32115         
32116         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32117          
32118         // position the brick
32119         var position = {
32120             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32121             y: this.currentSize.y + minimumY + this.padHeight
32122         };
32123         
32124         Roo.log(position);
32125         // apply setHeight to necessary columns
32126         var setHeight = minimumY + sz.height + this.padHeight;
32127         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32128         
32129         var setSpan = this.cols + 1 - colGroup.length;
32130         for ( var i = 0; i < setSpan; i++ ) {
32131           this.colYs[ shortColIndex + i ] = setHeight ;
32132         }
32133       
32134         return position;
32135     },
32136     
32137     /**
32138      * @param {Number} colSpan - number of columns the element spans
32139      * @returns {Array} colGroup
32140      */
32141     _getColGroup : function( colSpan )
32142     {
32143         if ( colSpan < 2 ) {
32144           // if brick spans only one column, use all the column Ys
32145           return this.colYs;
32146         }
32147       
32148         var colGroup = [];
32149         // how many different places could this brick fit horizontally
32150         var groupCount = this.cols + 1 - colSpan;
32151         // for each group potential horizontal position
32152         for ( var i = 0; i < groupCount; i++ ) {
32153           // make an array of colY values for that one group
32154           var groupColYs = this.colYs.slice( i, i + colSpan );
32155           // and get the max value of the array
32156           colGroup[i] = Math.max.apply( Math, groupColYs );
32157         }
32158         return colGroup;
32159     },
32160     /*
32161     _manageStamp : function( stamp )
32162     {
32163         var stampSize =  stamp.getSize();
32164         var offset = stamp.getBox();
32165         // get the columns that this stamp affects
32166         var firstX = this.isOriginLeft ? offset.x : offset.right;
32167         var lastX = firstX + stampSize.width;
32168         var firstCol = Math.floor( firstX / this.columnWidth );
32169         firstCol = Math.max( 0, firstCol );
32170         
32171         var lastCol = Math.floor( lastX / this.columnWidth );
32172         // lastCol should not go over if multiple of columnWidth #425
32173         lastCol -= lastX % this.columnWidth ? 0 : 1;
32174         lastCol = Math.min( this.cols - 1, lastCol );
32175         
32176         // set colYs to bottom of the stamp
32177         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32178             stampSize.height;
32179             
32180         for ( var i = firstCol; i <= lastCol; i++ ) {
32181           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32182         }
32183     },
32184     */
32185     
32186     _getContainerSize : function()
32187     {
32188         this.maxY = Math.max.apply( Math, this.colYs );
32189         var size = {
32190             height: this.maxY
32191         };
32192       
32193         if ( this.isFitWidth ) {
32194             size.width = this._getContainerFitWidth();
32195         }
32196       
32197         return size;
32198     },
32199     
32200     _getContainerFitWidth : function()
32201     {
32202         var unusedCols = 0;
32203         // count unused columns
32204         var i = this.cols;
32205         while ( --i ) {
32206           if ( this.colYs[i] !== 0 ) {
32207             break;
32208           }
32209           unusedCols++;
32210         }
32211         // fit container to columns that have been used
32212         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32213     },
32214     
32215     needsResizeLayout : function()
32216     {
32217         var previousWidth = this.containerWidth;
32218         this.getContainerWidth();
32219         return previousWidth !== this.containerWidth;
32220     }
32221  
32222 });
32223
32224  
32225
32226  /*
32227  * - LGPL
32228  *
32229  * element
32230  * 
32231  */
32232
32233 /**
32234  * @class Roo.bootstrap.MasonryBrick
32235  * @extends Roo.bootstrap.Component
32236  * Bootstrap MasonryBrick class
32237  * 
32238  * @constructor
32239  * Create a new MasonryBrick
32240  * @param {Object} config The config object
32241  */
32242
32243 Roo.bootstrap.MasonryBrick = function(config){
32244     
32245     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32246     
32247     Roo.bootstrap.MasonryBrick.register(this);
32248     
32249     this.addEvents({
32250         // raw events
32251         /**
32252          * @event click
32253          * When a MasonryBrick is clcik
32254          * @param {Roo.bootstrap.MasonryBrick} this
32255          * @param {Roo.EventObject} e
32256          */
32257         "click" : true
32258     });
32259 };
32260
32261 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32262     
32263     /**
32264      * @cfg {String} title
32265      */   
32266     title : '',
32267     /**
32268      * @cfg {String} html
32269      */   
32270     html : '',
32271     /**
32272      * @cfg {String} bgimage
32273      */   
32274     bgimage : '',
32275     /**
32276      * @cfg {String} videourl
32277      */   
32278     videourl : '',
32279     /**
32280      * @cfg {String} cls
32281      */   
32282     cls : '',
32283     /**
32284      * @cfg {String} href
32285      */   
32286     href : '',
32287     /**
32288      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32289      */   
32290     size : 'xs',
32291     
32292     /**
32293      * @cfg {String} placetitle (center|bottom)
32294      */   
32295     placetitle : '',
32296     
32297     /**
32298      * @cfg {Boolean} isFitContainer defalut true
32299      */   
32300     isFitContainer : true, 
32301     
32302     /**
32303      * @cfg {Boolean} preventDefault defalut false
32304      */   
32305     preventDefault : false, 
32306     
32307     /**
32308      * @cfg {Boolean} inverse defalut false
32309      */   
32310     maskInverse : false, 
32311     
32312     getAutoCreate : function()
32313     {
32314         if(!this.isFitContainer){
32315             return this.getSplitAutoCreate();
32316         }
32317         
32318         var cls = 'masonry-brick masonry-brick-full';
32319         
32320         if(this.href.length){
32321             cls += ' masonry-brick-link';
32322         }
32323         
32324         if(this.bgimage.length){
32325             cls += ' masonry-brick-image';
32326         }
32327         
32328         if(this.maskInverse){
32329             cls += ' mask-inverse';
32330         }
32331         
32332         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32333             cls += ' enable-mask';
32334         }
32335         
32336         if(this.size){
32337             cls += ' masonry-' + this.size + '-brick';
32338         }
32339         
32340         if(this.placetitle.length){
32341             
32342             switch (this.placetitle) {
32343                 case 'center' :
32344                     cls += ' masonry-center-title';
32345                     break;
32346                 case 'bottom' :
32347                     cls += ' masonry-bottom-title';
32348                     break;
32349                 default:
32350                     break;
32351             }
32352             
32353         } else {
32354             if(!this.html.length && !this.bgimage.length){
32355                 cls += ' masonry-center-title';
32356             }
32357
32358             if(!this.html.length && this.bgimage.length){
32359                 cls += ' masonry-bottom-title';
32360             }
32361         }
32362         
32363         if(this.cls){
32364             cls += ' ' + this.cls;
32365         }
32366         
32367         var cfg = {
32368             tag: (this.href.length) ? 'a' : 'div',
32369             cls: cls,
32370             cn: [
32371                 {
32372                     tag: 'div',
32373                     cls: 'masonry-brick-mask'
32374                 },
32375                 {
32376                     tag: 'div',
32377                     cls: 'masonry-brick-paragraph',
32378                     cn: []
32379                 }
32380             ]
32381         };
32382         
32383         if(this.href.length){
32384             cfg.href = this.href;
32385         }
32386         
32387         var cn = cfg.cn[1].cn;
32388         
32389         if(this.title.length){
32390             cn.push({
32391                 tag: 'h4',
32392                 cls: 'masonry-brick-title',
32393                 html: this.title
32394             });
32395         }
32396         
32397         if(this.html.length){
32398             cn.push({
32399                 tag: 'p',
32400                 cls: 'masonry-brick-text',
32401                 html: this.html
32402             });
32403         }
32404         
32405         if (!this.title.length && !this.html.length) {
32406             cfg.cn[1].cls += ' hide';
32407         }
32408         
32409         if(this.bgimage.length){
32410             cfg.cn.push({
32411                 tag: 'img',
32412                 cls: 'masonry-brick-image-view',
32413                 src: this.bgimage
32414             });
32415         }
32416         
32417         if(this.videourl.length){
32418             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32419             // youtube support only?
32420             cfg.cn.push({
32421                 tag: 'iframe',
32422                 cls: 'masonry-brick-image-view',
32423                 src: vurl,
32424                 frameborder : 0,
32425                 allowfullscreen : true
32426             });
32427         }
32428         
32429         return cfg;
32430         
32431     },
32432     
32433     getSplitAutoCreate : function()
32434     {
32435         var cls = 'masonry-brick masonry-brick-split';
32436         
32437         if(this.href.length){
32438             cls += ' masonry-brick-link';
32439         }
32440         
32441         if(this.bgimage.length){
32442             cls += ' masonry-brick-image';
32443         }
32444         
32445         if(this.size){
32446             cls += ' masonry-' + this.size + '-brick';
32447         }
32448         
32449         switch (this.placetitle) {
32450             case 'center' :
32451                 cls += ' masonry-center-title';
32452                 break;
32453             case 'bottom' :
32454                 cls += ' masonry-bottom-title';
32455                 break;
32456             default:
32457                 if(!this.bgimage.length){
32458                     cls += ' masonry-center-title';
32459                 }
32460
32461                 if(this.bgimage.length){
32462                     cls += ' masonry-bottom-title';
32463                 }
32464                 break;
32465         }
32466         
32467         if(this.cls){
32468             cls += ' ' + this.cls;
32469         }
32470         
32471         var cfg = {
32472             tag: (this.href.length) ? 'a' : 'div',
32473             cls: cls,
32474             cn: [
32475                 {
32476                     tag: 'div',
32477                     cls: 'masonry-brick-split-head',
32478                     cn: [
32479                         {
32480                             tag: 'div',
32481                             cls: 'masonry-brick-paragraph',
32482                             cn: []
32483                         }
32484                     ]
32485                 },
32486                 {
32487                     tag: 'div',
32488                     cls: 'masonry-brick-split-body',
32489                     cn: []
32490                 }
32491             ]
32492         };
32493         
32494         if(this.href.length){
32495             cfg.href = this.href;
32496         }
32497         
32498         if(this.title.length){
32499             cfg.cn[0].cn[0].cn.push({
32500                 tag: 'h4',
32501                 cls: 'masonry-brick-title',
32502                 html: this.title
32503             });
32504         }
32505         
32506         if(this.html.length){
32507             cfg.cn[1].cn.push({
32508                 tag: 'p',
32509                 cls: 'masonry-brick-text',
32510                 html: this.html
32511             });
32512         }
32513
32514         if(this.bgimage.length){
32515             cfg.cn[0].cn.push({
32516                 tag: 'img',
32517                 cls: 'masonry-brick-image-view',
32518                 src: this.bgimage
32519             });
32520         }
32521         
32522         if(this.videourl.length){
32523             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32524             // youtube support only?
32525             cfg.cn[0].cn.cn.push({
32526                 tag: 'iframe',
32527                 cls: 'masonry-brick-image-view',
32528                 src: vurl,
32529                 frameborder : 0,
32530                 allowfullscreen : true
32531             });
32532         }
32533         
32534         return cfg;
32535     },
32536     
32537     initEvents: function() 
32538     {
32539         switch (this.size) {
32540             case 'xs' :
32541                 this.x = 1;
32542                 this.y = 1;
32543                 break;
32544             case 'sm' :
32545                 this.x = 2;
32546                 this.y = 2;
32547                 break;
32548             case 'md' :
32549             case 'md-left' :
32550             case 'md-right' :
32551                 this.x = 3;
32552                 this.y = 3;
32553                 break;
32554             case 'tall' :
32555                 this.x = 2;
32556                 this.y = 3;
32557                 break;
32558             case 'wide' :
32559                 this.x = 3;
32560                 this.y = 2;
32561                 break;
32562             case 'wide-thin' :
32563                 this.x = 3;
32564                 this.y = 1;
32565                 break;
32566                         
32567             default :
32568                 break;
32569         }
32570         
32571         if(Roo.isTouch){
32572             this.el.on('touchstart', this.onTouchStart, this);
32573             this.el.on('touchmove', this.onTouchMove, this);
32574             this.el.on('touchend', this.onTouchEnd, this);
32575             this.el.on('contextmenu', this.onContextMenu, this);
32576         } else {
32577             this.el.on('mouseenter'  ,this.enter, this);
32578             this.el.on('mouseleave', this.leave, this);
32579             this.el.on('click', this.onClick, this);
32580         }
32581         
32582         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32583             this.parent().bricks.push(this);   
32584         }
32585         
32586     },
32587     
32588     onClick: function(e, el)
32589     {
32590         var time = this.endTimer - this.startTimer;
32591         // Roo.log(e.preventDefault());
32592         if(Roo.isTouch){
32593             if(time > 1000){
32594                 e.preventDefault();
32595                 return;
32596             }
32597         }
32598         
32599         if(!this.preventDefault){
32600             return;
32601         }
32602         
32603         e.preventDefault();
32604         
32605         if (this.activcClass != '') {
32606             this.selectBrick();
32607         }
32608         
32609         this.fireEvent('click', this);
32610     },
32611     
32612     enter: function(e, el)
32613     {
32614         e.preventDefault();
32615         
32616         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32617             return;
32618         }
32619         
32620         if(this.bgimage.length && this.html.length){
32621             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32622         }
32623     },
32624     
32625     leave: function(e, el)
32626     {
32627         e.preventDefault();
32628         
32629         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32630             return;
32631         }
32632         
32633         if(this.bgimage.length && this.html.length){
32634             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32635         }
32636     },
32637     
32638     onTouchStart: function(e, el)
32639     {
32640 //        e.preventDefault();
32641         
32642         this.touchmoved = false;
32643         
32644         if(!this.isFitContainer){
32645             return;
32646         }
32647         
32648         if(!this.bgimage.length || !this.html.length){
32649             return;
32650         }
32651         
32652         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32653         
32654         this.timer = new Date().getTime();
32655         
32656     },
32657     
32658     onTouchMove: function(e, el)
32659     {
32660         this.touchmoved = true;
32661     },
32662     
32663     onContextMenu : function(e,el)
32664     {
32665         e.preventDefault();
32666         e.stopPropagation();
32667         return false;
32668     },
32669     
32670     onTouchEnd: function(e, el)
32671     {
32672 //        e.preventDefault();
32673         
32674         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32675         
32676             this.leave(e,el);
32677             
32678             return;
32679         }
32680         
32681         if(!this.bgimage.length || !this.html.length){
32682             
32683             if(this.href.length){
32684                 window.location.href = this.href;
32685             }
32686             
32687             return;
32688         }
32689         
32690         if(!this.isFitContainer){
32691             return;
32692         }
32693         
32694         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32695         
32696         window.location.href = this.href;
32697     },
32698     
32699     //selection on single brick only
32700     selectBrick : function() {
32701         
32702         if (!this.parentId) {
32703             return;
32704         }
32705         
32706         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32707         var index = m.selectedBrick.indexOf(this.id);
32708         
32709         if ( index > -1) {
32710             m.selectedBrick.splice(index,1);
32711             this.el.removeClass(this.activeClass);
32712             return;
32713         }
32714         
32715         for(var i = 0; i < m.selectedBrick.length; i++) {
32716             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32717             b.el.removeClass(b.activeClass);
32718         }
32719         
32720         m.selectedBrick = [];
32721         
32722         m.selectedBrick.push(this.id);
32723         this.el.addClass(this.activeClass);
32724         return;
32725     }
32726     
32727 });
32728
32729 Roo.apply(Roo.bootstrap.MasonryBrick, {
32730     
32731     //groups: {},
32732     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32733      /**
32734     * register a Masonry Brick
32735     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32736     */
32737     
32738     register : function(brick)
32739     {
32740         //this.groups[brick.id] = brick;
32741         this.groups.add(brick.id, brick);
32742     },
32743     /**
32744     * fetch a  masonry brick based on the masonry brick ID
32745     * @param {string} the masonry brick to add
32746     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32747     */
32748     
32749     get: function(brick_id) 
32750     {
32751         // if (typeof(this.groups[brick_id]) == 'undefined') {
32752         //     return false;
32753         // }
32754         // return this.groups[brick_id] ;
32755         
32756         if(this.groups.key(brick_id)) {
32757             return this.groups.key(brick_id);
32758         }
32759         
32760         return false;
32761     }
32762     
32763     
32764     
32765 });
32766
32767  /*
32768  * - LGPL
32769  *
32770  * element
32771  * 
32772  */
32773
32774 /**
32775  * @class Roo.bootstrap.Brick
32776  * @extends Roo.bootstrap.Component
32777  * Bootstrap Brick class
32778  * 
32779  * @constructor
32780  * Create a new Brick
32781  * @param {Object} config The config object
32782  */
32783
32784 Roo.bootstrap.Brick = function(config){
32785     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32786     
32787     this.addEvents({
32788         // raw events
32789         /**
32790          * @event click
32791          * When a Brick is click
32792          * @param {Roo.bootstrap.Brick} this
32793          * @param {Roo.EventObject} e
32794          */
32795         "click" : true
32796     });
32797 };
32798
32799 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32800     
32801     /**
32802      * @cfg {String} title
32803      */   
32804     title : '',
32805     /**
32806      * @cfg {String} html
32807      */   
32808     html : '',
32809     /**
32810      * @cfg {String} bgimage
32811      */   
32812     bgimage : '',
32813     /**
32814      * @cfg {String} cls
32815      */   
32816     cls : '',
32817     /**
32818      * @cfg {String} href
32819      */   
32820     href : '',
32821     /**
32822      * @cfg {String} video
32823      */   
32824     video : '',
32825     /**
32826      * @cfg {Boolean} square
32827      */   
32828     square : true,
32829     
32830     getAutoCreate : function()
32831     {
32832         var cls = 'roo-brick';
32833         
32834         if(this.href.length){
32835             cls += ' roo-brick-link';
32836         }
32837         
32838         if(this.bgimage.length){
32839             cls += ' roo-brick-image';
32840         }
32841         
32842         if(!this.html.length && !this.bgimage.length){
32843             cls += ' roo-brick-center-title';
32844         }
32845         
32846         if(!this.html.length && this.bgimage.length){
32847             cls += ' roo-brick-bottom-title';
32848         }
32849         
32850         if(this.cls){
32851             cls += ' ' + this.cls;
32852         }
32853         
32854         var cfg = {
32855             tag: (this.href.length) ? 'a' : 'div',
32856             cls: cls,
32857             cn: [
32858                 {
32859                     tag: 'div',
32860                     cls: 'roo-brick-paragraph',
32861                     cn: []
32862                 }
32863             ]
32864         };
32865         
32866         if(this.href.length){
32867             cfg.href = this.href;
32868         }
32869         
32870         var cn = cfg.cn[0].cn;
32871         
32872         if(this.title.length){
32873             cn.push({
32874                 tag: 'h4',
32875                 cls: 'roo-brick-title',
32876                 html: this.title
32877             });
32878         }
32879         
32880         if(this.html.length){
32881             cn.push({
32882                 tag: 'p',
32883                 cls: 'roo-brick-text',
32884                 html: this.html
32885             });
32886         } else {
32887             cn.cls += ' hide';
32888         }
32889         
32890         if(this.bgimage.length){
32891             cfg.cn.push({
32892                 tag: 'img',
32893                 cls: 'roo-brick-image-view',
32894                 src: this.bgimage
32895             });
32896         }
32897         
32898         return cfg;
32899     },
32900     
32901     initEvents: function() 
32902     {
32903         if(this.title.length || this.html.length){
32904             this.el.on('mouseenter'  ,this.enter, this);
32905             this.el.on('mouseleave', this.leave, this);
32906         }
32907         
32908         Roo.EventManager.onWindowResize(this.resize, this); 
32909         
32910         if(this.bgimage.length){
32911             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32912             this.imageEl.on('load', this.onImageLoad, this);
32913             return;
32914         }
32915         
32916         this.resize();
32917     },
32918     
32919     onImageLoad : function()
32920     {
32921         this.resize();
32922     },
32923     
32924     resize : function()
32925     {
32926         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32927         
32928         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32929         
32930         if(this.bgimage.length){
32931             var image = this.el.select('.roo-brick-image-view', true).first();
32932             
32933             image.setWidth(paragraph.getWidth());
32934             
32935             if(this.square){
32936                 image.setHeight(paragraph.getWidth());
32937             }
32938             
32939             this.el.setHeight(image.getHeight());
32940             paragraph.setHeight(image.getHeight());
32941             
32942         }
32943         
32944     },
32945     
32946     enter: function(e, el)
32947     {
32948         e.preventDefault();
32949         
32950         if(this.bgimage.length){
32951             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32952             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32953         }
32954     },
32955     
32956     leave: function(e, el)
32957     {
32958         e.preventDefault();
32959         
32960         if(this.bgimage.length){
32961             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32962             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32963         }
32964     }
32965     
32966 });
32967
32968  
32969
32970  /*
32971  * - LGPL
32972  *
32973  * Input
32974  * 
32975  */
32976
32977 /**
32978  * @class Roo.bootstrap.NumberField
32979  * @extends Roo.bootstrap.Input
32980  * Bootstrap NumberField class
32981  * 
32982  * 
32983  * 
32984  * 
32985  * @constructor
32986  * Create a new NumberField
32987  * @param {Object} config The config object
32988  */
32989
32990 Roo.bootstrap.NumberField = function(config){
32991     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32992 };
32993
32994 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32995     
32996     /**
32997      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32998      */
32999     allowDecimals : true,
33000     /**
33001      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33002      */
33003     decimalSeparator : ".",
33004     /**
33005      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33006      */
33007     decimalPrecision : 2,
33008     /**
33009      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33010      */
33011     allowNegative : true,
33012     /**
33013      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33014      */
33015     minValue : Number.NEGATIVE_INFINITY,
33016     /**
33017      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33018      */
33019     maxValue : Number.MAX_VALUE,
33020     /**
33021      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33022      */
33023     minText : "The minimum value for this field is {0}",
33024     /**
33025      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33026      */
33027     maxText : "The maximum value for this field is {0}",
33028     /**
33029      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33030      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33031      */
33032     nanText : "{0} is not a valid number",
33033     /**
33034      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33035      */
33036     castInt : true,
33037     /**
33038      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33039      */
33040     thousandsDelimiter : false,
33041     /**
33042      * @cfg {String} valueAlign alignment of value
33043      */
33044     valueAlign : "left",
33045
33046     getAutoCreate : function()
33047     {
33048         var hiddenInput = {
33049             tag: 'input',
33050             type: 'hidden',
33051             id: Roo.id(),
33052             cls: 'hidden-number-input'
33053         };
33054         
33055         if (this.name) {
33056             hiddenInput.name = this.name;
33057         }
33058         
33059         this.name = '';
33060         
33061         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33062         
33063         this.name = hiddenInput.name;
33064         
33065         if(cfg.cn.length > 0) {
33066             cfg.cn.push(hiddenInput);
33067         }
33068         
33069         return cfg;
33070     },
33071
33072     // private
33073     initEvents : function()
33074     {   
33075         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33076         
33077         var allowed = "0123456789";
33078         
33079         if(this.allowDecimals){
33080             allowed += this.decimalSeparator;
33081         }
33082         
33083         if(this.allowNegative){
33084             allowed += "-";
33085         }
33086         
33087         if(this.thousandsDelimiter) {
33088             allowed += ",";
33089         }
33090         
33091         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33092         
33093         var keyPress = function(e){
33094             
33095             var k = e.getKey();
33096             
33097             var c = e.getCharCode();
33098             
33099             if(
33100                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33101                     allowed.indexOf(String.fromCharCode(c)) === -1
33102             ){
33103                 e.stopEvent();
33104                 return;
33105             }
33106             
33107             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33108                 return;
33109             }
33110             
33111             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33112                 e.stopEvent();
33113             }
33114         };
33115         
33116         this.el.on("keypress", keyPress, this);
33117     },
33118     
33119     validateValue : function(value)
33120     {
33121         
33122         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33123             return false;
33124         }
33125         
33126         var num = this.parseValue(value);
33127         
33128         if(isNaN(num)){
33129             this.markInvalid(String.format(this.nanText, value));
33130             return false;
33131         }
33132         
33133         if(num < this.minValue){
33134             this.markInvalid(String.format(this.minText, this.minValue));
33135             return false;
33136         }
33137         
33138         if(num > this.maxValue){
33139             this.markInvalid(String.format(this.maxText, this.maxValue));
33140             return false;
33141         }
33142         
33143         return true;
33144     },
33145
33146     getValue : function()
33147     {
33148         var v = this.hiddenEl().getValue();
33149         
33150         return this.fixPrecision(this.parseValue(v));
33151     },
33152
33153     parseValue : function(value)
33154     {
33155         if(this.thousandsDelimiter) {
33156             value += "";
33157             r = new RegExp(",", "g");
33158             value = value.replace(r, "");
33159         }
33160         
33161         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33162         return isNaN(value) ? '' : value;
33163     },
33164
33165     fixPrecision : function(value)
33166     {
33167         if(this.thousandsDelimiter) {
33168             value += "";
33169             r = new RegExp(",", "g");
33170             value = value.replace(r, "");
33171         }
33172         
33173         var nan = isNaN(value);
33174         
33175         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33176             return nan ? '' : value;
33177         }
33178         return parseFloat(value).toFixed(this.decimalPrecision);
33179     },
33180
33181     setValue : function(v)
33182     {
33183         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33184         
33185         this.value = v;
33186         
33187         if(this.rendered){
33188             
33189             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33190             
33191             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
33192                 this.thousandsDelimiter || ''
33193             );
33194             
33195             if(this.allowBlank && !v) {
33196                 this.inputEl().dom.value = '';
33197             }
33198             
33199             this.validate();
33200         }
33201     },
33202
33203     decimalPrecisionFcn : function(v)
33204     {
33205         return Math.floor(v);
33206     },
33207
33208     beforeBlur : function()
33209     {
33210         if(!this.castInt){
33211             return;
33212         }
33213         
33214         var v = this.parseValue(this.getRawValue());
33215         if(v){
33216             this.setValue(v);
33217         }
33218     },
33219     
33220     hiddenEl : function()
33221     {
33222         return this.el.select('input.hidden-number-input',true).first();
33223     }
33224     
33225 });
33226
33227  
33228
33229 /*
33230 * Licence: LGPL
33231 */
33232
33233 /**
33234  * @class Roo.bootstrap.DocumentSlider
33235  * @extends Roo.bootstrap.Component
33236  * Bootstrap DocumentSlider class
33237  * 
33238  * @constructor
33239  * Create a new DocumentViewer
33240  * @param {Object} config The config object
33241  */
33242
33243 Roo.bootstrap.DocumentSlider = function(config){
33244     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33245     
33246     this.files = [];
33247     
33248     this.addEvents({
33249         /**
33250          * @event initial
33251          * Fire after initEvent
33252          * @param {Roo.bootstrap.DocumentSlider} this
33253          */
33254         "initial" : true,
33255         /**
33256          * @event update
33257          * Fire after update
33258          * @param {Roo.bootstrap.DocumentSlider} this
33259          */
33260         "update" : true,
33261         /**
33262          * @event click
33263          * Fire after click
33264          * @param {Roo.bootstrap.DocumentSlider} this
33265          */
33266         "click" : true
33267     });
33268 };
33269
33270 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33271     
33272     files : false,
33273     
33274     indicator : 0,
33275     
33276     getAutoCreate : function()
33277     {
33278         var cfg = {
33279             tag : 'div',
33280             cls : 'roo-document-slider',
33281             cn : [
33282                 {
33283                     tag : 'div',
33284                     cls : 'roo-document-slider-header',
33285                     cn : [
33286                         {
33287                             tag : 'div',
33288                             cls : 'roo-document-slider-header-title'
33289                         }
33290                     ]
33291                 },
33292                 {
33293                     tag : 'div',
33294                     cls : 'roo-document-slider-body',
33295                     cn : [
33296                         {
33297                             tag : 'div',
33298                             cls : 'roo-document-slider-prev',
33299                             cn : [
33300                                 {
33301                                     tag : 'i',
33302                                     cls : 'fa fa-chevron-left'
33303                                 }
33304                             ]
33305                         },
33306                         {
33307                             tag : 'div',
33308                             cls : 'roo-document-slider-thumb',
33309                             cn : [
33310                                 {
33311                                     tag : 'img',
33312                                     cls : 'roo-document-slider-image'
33313                                 }
33314                             ]
33315                         },
33316                         {
33317                             tag : 'div',
33318                             cls : 'roo-document-slider-next',
33319                             cn : [
33320                                 {
33321                                     tag : 'i',
33322                                     cls : 'fa fa-chevron-right'
33323                                 }
33324                             ]
33325                         }
33326                     ]
33327                 }
33328             ]
33329         };
33330         
33331         return cfg;
33332     },
33333     
33334     initEvents : function()
33335     {
33336         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33337         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33338         
33339         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33340         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33341         
33342         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33343         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33344         
33345         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33346         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33347         
33348         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33349         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33350         
33351         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33352         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33353         
33354         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33355         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33356         
33357         this.thumbEl.on('click', this.onClick, this);
33358         
33359         this.prevIndicator.on('click', this.prev, this);
33360         
33361         this.nextIndicator.on('click', this.next, this);
33362         
33363     },
33364     
33365     initial : function()
33366     {
33367         if(this.files.length){
33368             this.indicator = 1;
33369             this.update()
33370         }
33371         
33372         this.fireEvent('initial', this);
33373     },
33374     
33375     update : function()
33376     {
33377         this.imageEl.attr('src', this.files[this.indicator - 1]);
33378         
33379         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33380         
33381         this.prevIndicator.show();
33382         
33383         if(this.indicator == 1){
33384             this.prevIndicator.hide();
33385         }
33386         
33387         this.nextIndicator.show();
33388         
33389         if(this.indicator == this.files.length){
33390             this.nextIndicator.hide();
33391         }
33392         
33393         this.thumbEl.scrollTo('top');
33394         
33395         this.fireEvent('update', this);
33396     },
33397     
33398     onClick : function(e)
33399     {
33400         e.preventDefault();
33401         
33402         this.fireEvent('click', this);
33403     },
33404     
33405     prev : function(e)
33406     {
33407         e.preventDefault();
33408         
33409         this.indicator = Math.max(1, this.indicator - 1);
33410         
33411         this.update();
33412     },
33413     
33414     next : function(e)
33415     {
33416         e.preventDefault();
33417         
33418         this.indicator = Math.min(this.files.length, this.indicator + 1);
33419         
33420         this.update();
33421     }
33422 });
33423 /*
33424  * - LGPL
33425  *
33426  * RadioSet
33427  *
33428  *
33429  */
33430
33431 /**
33432  * @class Roo.bootstrap.RadioSet
33433  * @extends Roo.bootstrap.Input
33434  * Bootstrap RadioSet class
33435  * @cfg {String} indicatorpos (left|right) default left
33436  * @cfg {Boolean} inline (true|false) inline the element (default true)
33437  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33438  * @constructor
33439  * Create a new RadioSet
33440  * @param {Object} config The config object
33441  */
33442
33443 Roo.bootstrap.RadioSet = function(config){
33444     
33445     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33446     
33447     this.radioes = [];
33448     
33449     Roo.bootstrap.RadioSet.register(this);
33450     
33451     this.addEvents({
33452         /**
33453         * @event check
33454         * Fires when the element is checked or unchecked.
33455         * @param {Roo.bootstrap.RadioSet} this This radio
33456         * @param {Roo.bootstrap.Radio} item The checked item
33457         */
33458        check : true
33459     });
33460     
33461 };
33462
33463 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33464
33465     radioes : false,
33466     
33467     inline : true,
33468     
33469     weight : '',
33470     
33471     indicatorpos : 'left',
33472     
33473     getAutoCreate : function()
33474     {
33475         var label = {
33476             tag : 'label',
33477             cls : 'roo-radio-set-label',
33478             cn : [
33479                 {
33480                     tag : 'span',
33481                     html : this.fieldLabel
33482                 }
33483             ]
33484         };
33485         
33486         if(this.indicatorpos == 'left'){
33487             label.cn.unshift({
33488                 tag : 'i',
33489                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33490                 tooltip : 'This field is required'
33491             });
33492         } else {
33493             label.cn.push({
33494                 tag : 'i',
33495                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33496                 tooltip : 'This field is required'
33497             });
33498         }
33499         
33500         var items = {
33501             tag : 'div',
33502             cls : 'roo-radio-set-items'
33503         };
33504         
33505         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33506         
33507         if (align === 'left' && this.fieldLabel.length) {
33508             
33509             items = {
33510                 cls : "roo-radio-set-right", 
33511                 cn: [
33512                     items
33513                 ]
33514             };
33515             
33516             if(this.labelWidth > 12){
33517                 label.style = "width: " + this.labelWidth + 'px';
33518             }
33519             
33520             if(this.labelWidth < 13 && this.labelmd == 0){
33521                 this.labelmd = this.labelWidth;
33522             }
33523             
33524             if(this.labellg > 0){
33525                 label.cls += ' col-lg-' + this.labellg;
33526                 items.cls += ' col-lg-' + (12 - this.labellg);
33527             }
33528             
33529             if(this.labelmd > 0){
33530                 label.cls += ' col-md-' + this.labelmd;
33531                 items.cls += ' col-md-' + (12 - this.labelmd);
33532             }
33533             
33534             if(this.labelsm > 0){
33535                 label.cls += ' col-sm-' + this.labelsm;
33536                 items.cls += ' col-sm-' + (12 - this.labelsm);
33537             }
33538             
33539             if(this.labelxs > 0){
33540                 label.cls += ' col-xs-' + this.labelxs;
33541                 items.cls += ' col-xs-' + (12 - this.labelxs);
33542             }
33543         }
33544         
33545         var cfg = {
33546             tag : 'div',
33547             cls : 'roo-radio-set',
33548             cn : [
33549                 {
33550                     tag : 'input',
33551                     cls : 'roo-radio-set-input',
33552                     type : 'hidden',
33553                     name : this.name,
33554                     value : this.value ? this.value :  ''
33555                 },
33556                 label,
33557                 items
33558             ]
33559         };
33560         
33561         if(this.weight.length){
33562             cfg.cls += ' roo-radio-' + this.weight;
33563         }
33564         
33565         if(this.inline) {
33566             cfg.cls += ' roo-radio-set-inline';
33567         }
33568         
33569         var settings=this;
33570         ['xs','sm','md','lg'].map(function(size){
33571             if (settings[size]) {
33572                 cfg.cls += ' col-' + size + '-' + settings[size];
33573             }
33574         });
33575         
33576         return cfg;
33577         
33578     },
33579
33580     initEvents : function()
33581     {
33582         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33583         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33584         
33585         if(!this.fieldLabel.length){
33586             this.labelEl.hide();
33587         }
33588         
33589         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33590         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33591         
33592         this.indicatorEl().addClass('invisible');
33593         
33594         this.originalValue = this.getValue();
33595         
33596     },
33597     
33598     inputEl: function ()
33599     {
33600         return this.el.select('.roo-radio-set-input', true).first();
33601     },
33602     
33603     getChildContainer : function()
33604     {
33605         return this.itemsEl;
33606     },
33607     
33608     register : function(item)
33609     {
33610         this.radioes.push(item);
33611         
33612     },
33613     
33614     validate : function()
33615     {   
33616         var valid = false;
33617         
33618         Roo.each(this.radioes, function(i){
33619             if(!i.checked){
33620                 return;
33621             }
33622             
33623             valid = true;
33624             return false;
33625         });
33626         
33627         if(this.allowBlank) {
33628             return true;
33629         }
33630         
33631         if(this.disabled || valid){
33632             this.markValid();
33633             return true;
33634         }
33635         
33636         this.markInvalid();
33637         return false;
33638         
33639     },
33640     
33641     markValid : function()
33642     {
33643         if(this.labelEl.isVisible(true)){
33644             this.indicatorEl().removeClass('visible');
33645             this.indicatorEl().addClass('invisible');
33646         }
33647         
33648         this.el.removeClass([this.invalidClass, this.validClass]);
33649         this.el.addClass(this.validClass);
33650         
33651         this.fireEvent('valid', this);
33652     },
33653     
33654     markInvalid : function(msg)
33655     {
33656         if(this.allowBlank || this.disabled){
33657             return;
33658         }
33659         
33660         if(this.labelEl.isVisible(true)){
33661             this.indicatorEl().removeClass('invisible');
33662             this.indicatorEl().addClass('visible');
33663         }
33664         
33665         this.el.removeClass([this.invalidClass, this.validClass]);
33666         this.el.addClass(this.invalidClass);
33667         
33668         this.fireEvent('invalid', this, msg);
33669         
33670     },
33671     
33672     setValue : function(v, suppressEvent)
33673     {   
33674         if(this.value === v){
33675             return;
33676         }
33677         
33678         this.value = v;
33679         
33680         if(this.rendered){
33681             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33682         }
33683         
33684         Roo.each(this.radioes, function(i){
33685             i.checked = false;
33686             i.el.removeClass('checked');
33687         });
33688         
33689         Roo.each(this.radioes, function(i){
33690             
33691             if(i.value === v || i.value.toString() === v.toString()){
33692                 i.checked = true;
33693                 i.el.addClass('checked');
33694                 
33695                 if(suppressEvent !== true){
33696                     this.fireEvent('check', this, i);
33697                 }
33698                 
33699                 return false;
33700             }
33701             
33702         }, this);
33703         
33704         this.validate();
33705     },
33706     
33707     clearInvalid : function(){
33708         
33709         if(!this.el || this.preventMark){
33710             return;
33711         }
33712         
33713         this.el.removeClass([this.invalidClass]);
33714         
33715         this.fireEvent('valid', this);
33716     }
33717     
33718 });
33719
33720 Roo.apply(Roo.bootstrap.RadioSet, {
33721     
33722     groups: {},
33723     
33724     register : function(set)
33725     {
33726         this.groups[set.name] = set;
33727     },
33728     
33729     get: function(name) 
33730     {
33731         if (typeof(this.groups[name]) == 'undefined') {
33732             return false;
33733         }
33734         
33735         return this.groups[name] ;
33736     }
33737     
33738 });
33739 /*
33740  * Based on:
33741  * Ext JS Library 1.1.1
33742  * Copyright(c) 2006-2007, Ext JS, LLC.
33743  *
33744  * Originally Released Under LGPL - original licence link has changed is not relivant.
33745  *
33746  * Fork - LGPL
33747  * <script type="text/javascript">
33748  */
33749
33750
33751 /**
33752  * @class Roo.bootstrap.SplitBar
33753  * @extends Roo.util.Observable
33754  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33755  * <br><br>
33756  * Usage:
33757  * <pre><code>
33758 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33759                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33760 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33761 split.minSize = 100;
33762 split.maxSize = 600;
33763 split.animate = true;
33764 split.on('moved', splitterMoved);
33765 </code></pre>
33766  * @constructor
33767  * Create a new SplitBar
33768  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33769  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33770  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33771  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33772                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33773                         position of the SplitBar).
33774  */
33775 Roo.bootstrap.SplitBar = function(cfg){
33776     
33777     /** @private */
33778     
33779     //{
33780     //  dragElement : elm
33781     //  resizingElement: el,
33782         // optional..
33783     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33784     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33785         // existingProxy ???
33786     //}
33787     
33788     this.el = Roo.get(cfg.dragElement, true);
33789     this.el.dom.unselectable = "on";
33790     /** @private */
33791     this.resizingEl = Roo.get(cfg.resizingElement, true);
33792
33793     /**
33794      * @private
33795      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33797      * @type Number
33798      */
33799     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33800     
33801     /**
33802      * The minimum size of the resizing element. (Defaults to 0)
33803      * @type Number
33804      */
33805     this.minSize = 0;
33806     
33807     /**
33808      * The maximum size of the resizing element. (Defaults to 2000)
33809      * @type Number
33810      */
33811     this.maxSize = 2000;
33812     
33813     /**
33814      * Whether to animate the transition to the new size
33815      * @type Boolean
33816      */
33817     this.animate = false;
33818     
33819     /**
33820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33821      * @type Boolean
33822      */
33823     this.useShim = false;
33824     
33825     /** @private */
33826     this.shim = null;
33827     
33828     if(!cfg.existingProxy){
33829         /** @private */
33830         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33831     }else{
33832         this.proxy = Roo.get(cfg.existingProxy).dom;
33833     }
33834     /** @private */
33835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33836     
33837     /** @private */
33838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33839     
33840     /** @private */
33841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33842     
33843     /** @private */
33844     this.dragSpecs = {};
33845     
33846     /**
33847      * @private The adapter to use to positon and resize elements
33848      */
33849     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33850     this.adapter.init(this);
33851     
33852     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33853         /** @private */
33854         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33855         this.el.addClass("roo-splitbar-h");
33856     }else{
33857         /** @private */
33858         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33859         this.el.addClass("roo-splitbar-v");
33860     }
33861     
33862     this.addEvents({
33863         /**
33864          * @event resize
33865          * Fires when the splitter is moved (alias for {@link #event-moved})
33866          * @param {Roo.bootstrap.SplitBar} this
33867          * @param {Number} newSize the new width or height
33868          */
33869         "resize" : true,
33870         /**
33871          * @event moved
33872          * Fires when the splitter is moved
33873          * @param {Roo.bootstrap.SplitBar} this
33874          * @param {Number} newSize the new width or height
33875          */
33876         "moved" : true,
33877         /**
33878          * @event beforeresize
33879          * Fires before the splitter is dragged
33880          * @param {Roo.bootstrap.SplitBar} this
33881          */
33882         "beforeresize" : true,
33883
33884         "beforeapply" : true
33885     });
33886
33887     Roo.util.Observable.call(this);
33888 };
33889
33890 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33891     onStartProxyDrag : function(x, y){
33892         this.fireEvent("beforeresize", this);
33893         if(!this.overlay){
33894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33895             o.unselectable();
33896             o.enableDisplayMode("block");
33897             // all splitbars share the same overlay
33898             Roo.bootstrap.SplitBar.prototype.overlay = o;
33899         }
33900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33901         this.overlay.show();
33902         Roo.get(this.proxy).setDisplayed("block");
33903         var size = this.adapter.getElementSize(this);
33904         this.activeMinSize = this.getMinimumSize();;
33905         this.activeMaxSize = this.getMaximumSize();;
33906         var c1 = size - this.activeMinSize;
33907         var c2 = Math.max(this.activeMaxSize - size, 0);
33908         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33909             this.dd.resetConstraints();
33910             this.dd.setXConstraint(
33911                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33912                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33913             );
33914             this.dd.setYConstraint(0, 0);
33915         }else{
33916             this.dd.resetConstraints();
33917             this.dd.setXConstraint(0, 0);
33918             this.dd.setYConstraint(
33919                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33920                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33921             );
33922          }
33923         this.dragSpecs.startSize = size;
33924         this.dragSpecs.startPoint = [x, y];
33925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33926     },
33927     
33928     /** 
33929      * @private Called after the drag operation by the DDProxy
33930      */
33931     onEndProxyDrag : function(e){
33932         Roo.get(this.proxy).setDisplayed(false);
33933         var endPoint = Roo.lib.Event.getXY(e);
33934         if(this.overlay){
33935             this.overlay.hide();
33936         }
33937         var newSize;
33938         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33939             newSize = this.dragSpecs.startSize + 
33940                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33941                     endPoint[0] - this.dragSpecs.startPoint[0] :
33942                     this.dragSpecs.startPoint[0] - endPoint[0]
33943                 );
33944         }else{
33945             newSize = this.dragSpecs.startSize + 
33946                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33947                     endPoint[1] - this.dragSpecs.startPoint[1] :
33948                     this.dragSpecs.startPoint[1] - endPoint[1]
33949                 );
33950         }
33951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33952         if(newSize != this.dragSpecs.startSize){
33953             if(this.fireEvent('beforeapply', this, newSize) !== false){
33954                 this.adapter.setElementSize(this, newSize);
33955                 this.fireEvent("moved", this, newSize);
33956                 this.fireEvent("resize", this, newSize);
33957             }
33958         }
33959     },
33960     
33961     /**
33962      * Get the adapter this SplitBar uses
33963      * @return The adapter object
33964      */
33965     getAdapter : function(){
33966         return this.adapter;
33967     },
33968     
33969     /**
33970      * Set the adapter this SplitBar uses
33971      * @param {Object} adapter A SplitBar adapter object
33972      */
33973     setAdapter : function(adapter){
33974         this.adapter = adapter;
33975         this.adapter.init(this);
33976     },
33977     
33978     /**
33979      * Gets the minimum size for the resizing element
33980      * @return {Number} The minimum size
33981      */
33982     getMinimumSize : function(){
33983         return this.minSize;
33984     },
33985     
33986     /**
33987      * Sets the minimum size for the resizing element
33988      * @param {Number} minSize The minimum size
33989      */
33990     setMinimumSize : function(minSize){
33991         this.minSize = minSize;
33992     },
33993     
33994     /**
33995      * Gets the maximum size for the resizing element
33996      * @return {Number} The maximum size
33997      */
33998     getMaximumSize : function(){
33999         return this.maxSize;
34000     },
34001     
34002     /**
34003      * Sets the maximum size for the resizing element
34004      * @param {Number} maxSize The maximum size
34005      */
34006     setMaximumSize : function(maxSize){
34007         this.maxSize = maxSize;
34008     },
34009     
34010     /**
34011      * Sets the initialize size for the resizing element
34012      * @param {Number} size The initial size
34013      */
34014     setCurrentSize : function(size){
34015         var oldAnimate = this.animate;
34016         this.animate = false;
34017         this.adapter.setElementSize(this, size);
34018         this.animate = oldAnimate;
34019     },
34020     
34021     /**
34022      * Destroy this splitbar. 
34023      * @param {Boolean} removeEl True to remove the element
34024      */
34025     destroy : function(removeEl){
34026         if(this.shim){
34027             this.shim.remove();
34028         }
34029         this.dd.unreg();
34030         this.proxy.parentNode.removeChild(this.proxy);
34031         if(removeEl){
34032             this.el.remove();
34033         }
34034     }
34035 });
34036
34037 /**
34038  * @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.
34039  */
34040 Roo.bootstrap.SplitBar.createProxy = function(dir){
34041     var proxy = new Roo.Element(document.createElement("div"));
34042     proxy.unselectable();
34043     var cls = 'roo-splitbar-proxy';
34044     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34045     document.body.appendChild(proxy.dom);
34046     return proxy.dom;
34047 };
34048
34049 /** 
34050  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34051  * Default Adapter. It assumes the splitter and resizing element are not positioned
34052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34053  */
34054 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34055 };
34056
34057 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34058     // do nothing for now
34059     init : function(s){
34060     
34061     },
34062     /**
34063      * Called before drag operations to get the current size of the resizing element. 
34064      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34065      */
34066      getElementSize : function(s){
34067         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34068             return s.resizingEl.getWidth();
34069         }else{
34070             return s.resizingEl.getHeight();
34071         }
34072     },
34073     
34074     /**
34075      * Called after drag operations to set the size of the resizing element.
34076      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34077      * @param {Number} newSize The new size to set
34078      * @param {Function} onComplete A function to be invoked when resizing is complete
34079      */
34080     setElementSize : function(s, newSize, onComplete){
34081         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34082             if(!s.animate){
34083                 s.resizingEl.setWidth(newSize);
34084                 if(onComplete){
34085                     onComplete(s, newSize);
34086                 }
34087             }else{
34088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34089             }
34090         }else{
34091             
34092             if(!s.animate){
34093                 s.resizingEl.setHeight(newSize);
34094                 if(onComplete){
34095                     onComplete(s, newSize);
34096                 }
34097             }else{
34098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34099             }
34100         }
34101     }
34102 };
34103
34104 /** 
34105  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34106  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34107  * Adapter that  moves the splitter element to align with the resized sizing element. 
34108  * Used with an absolute positioned SplitBar.
34109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34110  * document.body, make sure you assign an id to the body element.
34111  */
34112 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34113     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34114     this.container = Roo.get(container);
34115 };
34116
34117 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34118     init : function(s){
34119         this.basic.init(s);
34120     },
34121     
34122     getElementSize : function(s){
34123         return this.basic.getElementSize(s);
34124     },
34125     
34126     setElementSize : function(s, newSize, onComplete){
34127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34128     },
34129     
34130     moveSplitter : function(s){
34131         var yes = Roo.bootstrap.SplitBar;
34132         switch(s.placement){
34133             case yes.LEFT:
34134                 s.el.setX(s.resizingEl.getRight());
34135                 break;
34136             case yes.RIGHT:
34137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34138                 break;
34139             case yes.TOP:
34140                 s.el.setY(s.resizingEl.getBottom());
34141                 break;
34142             case yes.BOTTOM:
34143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34144                 break;
34145         }
34146     }
34147 };
34148
34149 /**
34150  * Orientation constant - Create a vertical SplitBar
34151  * @static
34152  * @type Number
34153  */
34154 Roo.bootstrap.SplitBar.VERTICAL = 1;
34155
34156 /**
34157  * Orientation constant - Create a horizontal SplitBar
34158  * @static
34159  * @type Number
34160  */
34161 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34162
34163 /**
34164  * Placement constant - The resizing element is to the left of the splitter element
34165  * @static
34166  * @type Number
34167  */
34168 Roo.bootstrap.SplitBar.LEFT = 1;
34169
34170 /**
34171  * Placement constant - The resizing element is to the right of the splitter element
34172  * @static
34173  * @type Number
34174  */
34175 Roo.bootstrap.SplitBar.RIGHT = 2;
34176
34177 /**
34178  * Placement constant - The resizing element is positioned above the splitter element
34179  * @static
34180  * @type Number
34181  */
34182 Roo.bootstrap.SplitBar.TOP = 3;
34183
34184 /**
34185  * Placement constant - The resizing element is positioned under splitter element
34186  * @static
34187  * @type Number
34188  */
34189 Roo.bootstrap.SplitBar.BOTTOM = 4;
34190 Roo.namespace("Roo.bootstrap.layout");/*
34191  * Based on:
34192  * Ext JS Library 1.1.1
34193  * Copyright(c) 2006-2007, Ext JS, LLC.
34194  *
34195  * Originally Released Under LGPL - original licence link has changed is not relivant.
34196  *
34197  * Fork - LGPL
34198  * <script type="text/javascript">
34199  */
34200
34201 /**
34202  * @class Roo.bootstrap.layout.Manager
34203  * @extends Roo.bootstrap.Component
34204  * Base class for layout managers.
34205  */
34206 Roo.bootstrap.layout.Manager = function(config)
34207 {
34208     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34209
34210
34211
34212
34213
34214     /** false to disable window resize monitoring @type Boolean */
34215     this.monitorWindowResize = true;
34216     this.regions = {};
34217     this.addEvents({
34218         /**
34219          * @event layout
34220          * Fires when a layout is performed.
34221          * @param {Roo.LayoutManager} this
34222          */
34223         "layout" : true,
34224         /**
34225          * @event regionresized
34226          * Fires when the user resizes a region.
34227          * @param {Roo.LayoutRegion} region The resized region
34228          * @param {Number} newSize The new size (width for east/west, height for north/south)
34229          */
34230         "regionresized" : true,
34231         /**
34232          * @event regioncollapsed
34233          * Fires when a region is collapsed.
34234          * @param {Roo.LayoutRegion} region The collapsed region
34235          */
34236         "regioncollapsed" : true,
34237         /**
34238          * @event regionexpanded
34239          * Fires when a region is expanded.
34240          * @param {Roo.LayoutRegion} region The expanded region
34241          */
34242         "regionexpanded" : true
34243     });
34244     this.updating = false;
34245
34246     if (config.el) {
34247         this.el = Roo.get(config.el);
34248         this.initEvents();
34249     }
34250
34251 };
34252
34253 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34254
34255
34256     regions : null,
34257
34258     monitorWindowResize : true,
34259
34260
34261     updating : false,
34262
34263
34264     onRender : function(ct, position)
34265     {
34266         if(!this.el){
34267             this.el = Roo.get(ct);
34268             this.initEvents();
34269         }
34270         //this.fireEvent('render',this);
34271     },
34272
34273
34274     initEvents: function()
34275     {
34276
34277
34278         // ie scrollbar fix
34279         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34280             document.body.scroll = "no";
34281         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34282             this.el.position('relative');
34283         }
34284         this.id = this.el.id;
34285         this.el.addClass("roo-layout-container");
34286         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34287         if(this.el.dom != document.body ) {
34288             this.el.on('resize', this.layout,this);
34289             this.el.on('show', this.layout,this);
34290         }
34291
34292     },
34293
34294     /**
34295      * Returns true if this layout is currently being updated
34296      * @return {Boolean}
34297      */
34298     isUpdating : function(){
34299         return this.updating;
34300     },
34301
34302     /**
34303      * Suspend the LayoutManager from doing auto-layouts while
34304      * making multiple add or remove calls
34305      */
34306     beginUpdate : function(){
34307         this.updating = true;
34308     },
34309
34310     /**
34311      * Restore auto-layouts and optionally disable the manager from performing a layout
34312      * @param {Boolean} noLayout true to disable a layout update
34313      */
34314     endUpdate : function(noLayout){
34315         this.updating = false;
34316         if(!noLayout){
34317             this.layout();
34318         }
34319     },
34320
34321     layout: function(){
34322         // abstract...
34323     },
34324
34325     onRegionResized : function(region, newSize){
34326         this.fireEvent("regionresized", region, newSize);
34327         this.layout();
34328     },
34329
34330     onRegionCollapsed : function(region){
34331         this.fireEvent("regioncollapsed", region);
34332     },
34333
34334     onRegionExpanded : function(region){
34335         this.fireEvent("regionexpanded", region);
34336     },
34337
34338     /**
34339      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34340      * performs box-model adjustments.
34341      * @return {Object} The size as an object {width: (the width), height: (the height)}
34342      */
34343     getViewSize : function()
34344     {
34345         var size;
34346         if(this.el.dom != document.body){
34347             size = this.el.getSize();
34348         }else{
34349             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34350         }
34351         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34352         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34353         return size;
34354     },
34355
34356     /**
34357      * Returns the Element this layout is bound to.
34358      * @return {Roo.Element}
34359      */
34360     getEl : function(){
34361         return this.el;
34362     },
34363
34364     /**
34365      * Returns the specified region.
34366      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34367      * @return {Roo.LayoutRegion}
34368      */
34369     getRegion : function(target){
34370         return this.regions[target.toLowerCase()];
34371     },
34372
34373     onWindowResize : function(){
34374         if(this.monitorWindowResize){
34375             this.layout();
34376         }
34377     }
34378 });
34379 /*
34380  * Based on:
34381  * Ext JS Library 1.1.1
34382  * Copyright(c) 2006-2007, Ext JS, LLC.
34383  *
34384  * Originally Released Under LGPL - original licence link has changed is not relivant.
34385  *
34386  * Fork - LGPL
34387  * <script type="text/javascript">
34388  */
34389 /**
34390  * @class Roo.bootstrap.layout.Border
34391  * @extends Roo.bootstrap.layout.Manager
34392  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34393  * please see: examples/bootstrap/nested.html<br><br>
34394  
34395 <b>The container the layout is rendered into can be either the body element or any other element.
34396 If it is not the body element, the container needs to either be an absolute positioned element,
34397 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34398 the container size if it is not the body element.</b>
34399
34400 * @constructor
34401 * Create a new Border
34402 * @param {Object} config Configuration options
34403  */
34404 Roo.bootstrap.layout.Border = function(config){
34405     config = config || {};
34406     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34407     
34408     
34409     
34410     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34411         if(config[region]){
34412             config[region].region = region;
34413             this.addRegion(config[region]);
34414         }
34415     },this);
34416     
34417 };
34418
34419 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34420
34421 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34422     /**
34423      * Creates and adds a new region if it doesn't already exist.
34424      * @param {String} target The target region key (north, south, east, west or center).
34425      * @param {Object} config The regions config object
34426      * @return {BorderLayoutRegion} The new region
34427      */
34428     addRegion : function(config)
34429     {
34430         if(!this.regions[config.region]){
34431             var r = this.factory(config);
34432             this.bindRegion(r);
34433         }
34434         return this.regions[config.region];
34435     },
34436
34437     // private (kinda)
34438     bindRegion : function(r){
34439         this.regions[r.config.region] = r;
34440         
34441         r.on("visibilitychange",    this.layout, this);
34442         r.on("paneladded",          this.layout, this);
34443         r.on("panelremoved",        this.layout, this);
34444         r.on("invalidated",         this.layout, this);
34445         r.on("resized",             this.onRegionResized, this);
34446         r.on("collapsed",           this.onRegionCollapsed, this);
34447         r.on("expanded",            this.onRegionExpanded, this);
34448     },
34449
34450     /**
34451      * Performs a layout update.
34452      */
34453     layout : function()
34454     {
34455         if(this.updating) {
34456             return;
34457         }
34458         
34459         // render all the rebions if they have not been done alreayd?
34460         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34461             if(this.regions[region] && !this.regions[region].bodyEl){
34462                 this.regions[region].onRender(this.el)
34463             }
34464         },this);
34465         
34466         var size = this.getViewSize();
34467         var w = size.width;
34468         var h = size.height;
34469         var centerW = w;
34470         var centerH = h;
34471         var centerY = 0;
34472         var centerX = 0;
34473         //var x = 0, y = 0;
34474
34475         var rs = this.regions;
34476         var north = rs["north"];
34477         var south = rs["south"]; 
34478         var west = rs["west"];
34479         var east = rs["east"];
34480         var center = rs["center"];
34481         //if(this.hideOnLayout){ // not supported anymore
34482             //c.el.setStyle("display", "none");
34483         //}
34484         if(north && north.isVisible()){
34485             var b = north.getBox();
34486             var m = north.getMargins();
34487             b.width = w - (m.left+m.right);
34488             b.x = m.left;
34489             b.y = m.top;
34490             centerY = b.height + b.y + m.bottom;
34491             centerH -= centerY;
34492             north.updateBox(this.safeBox(b));
34493         }
34494         if(south && south.isVisible()){
34495             var b = south.getBox();
34496             var m = south.getMargins();
34497             b.width = w - (m.left+m.right);
34498             b.x = m.left;
34499             var totalHeight = (b.height + m.top + m.bottom);
34500             b.y = h - totalHeight + m.top;
34501             centerH -= totalHeight;
34502             south.updateBox(this.safeBox(b));
34503         }
34504         if(west && west.isVisible()){
34505             var b = west.getBox();
34506             var m = west.getMargins();
34507             b.height = centerH - (m.top+m.bottom);
34508             b.x = m.left;
34509             b.y = centerY + m.top;
34510             var totalWidth = (b.width + m.left + m.right);
34511             centerX += totalWidth;
34512             centerW -= totalWidth;
34513             west.updateBox(this.safeBox(b));
34514         }
34515         if(east && east.isVisible()){
34516             var b = east.getBox();
34517             var m = east.getMargins();
34518             b.height = centerH - (m.top+m.bottom);
34519             var totalWidth = (b.width + m.left + m.right);
34520             b.x = w - totalWidth + m.left;
34521             b.y = centerY + m.top;
34522             centerW -= totalWidth;
34523             east.updateBox(this.safeBox(b));
34524         }
34525         if(center){
34526             var m = center.getMargins();
34527             var centerBox = {
34528                 x: centerX + m.left,
34529                 y: centerY + m.top,
34530                 width: centerW - (m.left+m.right),
34531                 height: centerH - (m.top+m.bottom)
34532             };
34533             //if(this.hideOnLayout){
34534                 //center.el.setStyle("display", "block");
34535             //}
34536             center.updateBox(this.safeBox(centerBox));
34537         }
34538         this.el.repaint();
34539         this.fireEvent("layout", this);
34540     },
34541
34542     // private
34543     safeBox : function(box){
34544         box.width = Math.max(0, box.width);
34545         box.height = Math.max(0, box.height);
34546         return box;
34547     },
34548
34549     /**
34550      * Adds a ContentPanel (or subclass) to this layout.
34551      * @param {String} target The target region key (north, south, east, west or center).
34552      * @param {Roo.ContentPanel} panel The panel to add
34553      * @return {Roo.ContentPanel} The added panel
34554      */
34555     add : function(target, panel){
34556          
34557         target = target.toLowerCase();
34558         return this.regions[target].add(panel);
34559     },
34560
34561     /**
34562      * Remove a ContentPanel (or subclass) to this layout.
34563      * @param {String} target The target region key (north, south, east, west or center).
34564      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34565      * @return {Roo.ContentPanel} The removed panel
34566      */
34567     remove : function(target, panel){
34568         target = target.toLowerCase();
34569         return this.regions[target].remove(panel);
34570     },
34571
34572     /**
34573      * Searches all regions for a panel with the specified id
34574      * @param {String} panelId
34575      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34576      */
34577     findPanel : function(panelId){
34578         var rs = this.regions;
34579         for(var target in rs){
34580             if(typeof rs[target] != "function"){
34581                 var p = rs[target].getPanel(panelId);
34582                 if(p){
34583                     return p;
34584                 }
34585             }
34586         }
34587         return null;
34588     },
34589
34590     /**
34591      * Searches all regions for a panel with the specified id and activates (shows) it.
34592      * @param {String/ContentPanel} panelId The panels id or the panel itself
34593      * @return {Roo.ContentPanel} The shown panel or null
34594      */
34595     showPanel : function(panelId) {
34596       var rs = this.regions;
34597       for(var target in rs){
34598          var r = rs[target];
34599          if(typeof r != "function"){
34600             if(r.hasPanel(panelId)){
34601                return r.showPanel(panelId);
34602             }
34603          }
34604       }
34605       return null;
34606    },
34607
34608    /**
34609      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34610      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34611      */
34612    /*
34613     restoreState : function(provider){
34614         if(!provider){
34615             provider = Roo.state.Manager;
34616         }
34617         var sm = new Roo.LayoutStateManager();
34618         sm.init(this, provider);
34619     },
34620 */
34621  
34622  
34623     /**
34624      * Adds a xtype elements to the layout.
34625      * <pre><code>
34626
34627 layout.addxtype({
34628        xtype : 'ContentPanel',
34629        region: 'west',
34630        items: [ .... ]
34631    }
34632 );
34633
34634 layout.addxtype({
34635         xtype : 'NestedLayoutPanel',
34636         region: 'west',
34637         layout: {
34638            center: { },
34639            west: { }   
34640         },
34641         items : [ ... list of content panels or nested layout panels.. ]
34642    }
34643 );
34644 </code></pre>
34645      * @param {Object} cfg Xtype definition of item to add.
34646      */
34647     addxtype : function(cfg)
34648     {
34649         // basically accepts a pannel...
34650         // can accept a layout region..!?!?
34651         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34652         
34653         
34654         // theory?  children can only be panels??
34655         
34656         //if (!cfg.xtype.match(/Panel$/)) {
34657         //    return false;
34658         //}
34659         var ret = false;
34660         
34661         if (typeof(cfg.region) == 'undefined') {
34662             Roo.log("Failed to add Panel, region was not set");
34663             Roo.log(cfg);
34664             return false;
34665         }
34666         var region = cfg.region;
34667         delete cfg.region;
34668         
34669           
34670         var xitems = [];
34671         if (cfg.items) {
34672             xitems = cfg.items;
34673             delete cfg.items;
34674         }
34675         var nb = false;
34676         
34677         switch(cfg.xtype) 
34678         {
34679             case 'Content':  // ContentPanel (el, cfg)
34680             case 'Scroll':  // ContentPanel (el, cfg)
34681             case 'View': 
34682                 cfg.autoCreate = true;
34683                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34684                 //} else {
34685                 //    var el = this.el.createChild();
34686                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34687                 //}
34688                 
34689                 this.add(region, ret);
34690                 break;
34691             
34692             /*
34693             case 'TreePanel': // our new panel!
34694                 cfg.el = this.el.createChild();
34695                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34696                 this.add(region, ret);
34697                 break;
34698             */
34699             
34700             case 'Nest': 
34701                 // create a new Layout (which is  a Border Layout...
34702                 
34703                 var clayout = cfg.layout;
34704                 clayout.el  = this.el.createChild();
34705                 clayout.items   = clayout.items  || [];
34706                 
34707                 delete cfg.layout;
34708                 
34709                 // replace this exitems with the clayout ones..
34710                 xitems = clayout.items;
34711                  
34712                 // force background off if it's in center...
34713                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34714                     cfg.background = false;
34715                 }
34716                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34717                 
34718                 
34719                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34720                 //console.log('adding nested layout panel '  + cfg.toSource());
34721                 this.add(region, ret);
34722                 nb = {}; /// find first...
34723                 break;
34724             
34725             case 'Grid':
34726                 
34727                 // needs grid and region
34728                 
34729                 //var el = this.getRegion(region).el.createChild();
34730                 /*
34731                  *var el = this.el.createChild();
34732                 // create the grid first...
34733                 cfg.grid.container = el;
34734                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34735                 */
34736                 
34737                 if (region == 'center' && this.active ) {
34738                     cfg.background = false;
34739                 }
34740                 
34741                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34742                 
34743                 this.add(region, ret);
34744                 /*
34745                 if (cfg.background) {
34746                     // render grid on panel activation (if panel background)
34747                     ret.on('activate', function(gp) {
34748                         if (!gp.grid.rendered) {
34749                     //        gp.grid.render(el);
34750                         }
34751                     });
34752                 } else {
34753                   //  cfg.grid.render(el);
34754                 }
34755                 */
34756                 break;
34757            
34758            
34759             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34760                 // it was the old xcomponent building that caused this before.
34761                 // espeically if border is the top element in the tree.
34762                 ret = this;
34763                 break; 
34764                 
34765                     
34766                 
34767                 
34768                 
34769             default:
34770                 /*
34771                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34772                     
34773                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34774                     this.add(region, ret);
34775                 } else {
34776                 */
34777                     Roo.log(cfg);
34778                     throw "Can not add '" + cfg.xtype + "' to Border";
34779                     return null;
34780              
34781                                 
34782              
34783         }
34784         this.beginUpdate();
34785         // add children..
34786         var region = '';
34787         var abn = {};
34788         Roo.each(xitems, function(i)  {
34789             region = nb && i.region ? i.region : false;
34790             
34791             var add = ret.addxtype(i);
34792            
34793             if (region) {
34794                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34795                 if (!i.background) {
34796                     abn[region] = nb[region] ;
34797                 }
34798             }
34799             
34800         });
34801         this.endUpdate();
34802
34803         // make the last non-background panel active..
34804         //if (nb) { Roo.log(abn); }
34805         if (nb) {
34806             
34807             for(var r in abn) {
34808                 region = this.getRegion(r);
34809                 if (region) {
34810                     // tried using nb[r], but it does not work..
34811                      
34812                     region.showPanel(abn[r]);
34813                    
34814                 }
34815             }
34816         }
34817         return ret;
34818         
34819     },
34820     
34821     
34822 // private
34823     factory : function(cfg)
34824     {
34825         
34826         var validRegions = Roo.bootstrap.layout.Border.regions;
34827
34828         var target = cfg.region;
34829         cfg.mgr = this;
34830         
34831         var r = Roo.bootstrap.layout;
34832         Roo.log(target);
34833         switch(target){
34834             case "north":
34835                 return new r.North(cfg);
34836             case "south":
34837                 return new r.South(cfg);
34838             case "east":
34839                 return new r.East(cfg);
34840             case "west":
34841                 return new r.West(cfg);
34842             case "center":
34843                 return new r.Center(cfg);
34844         }
34845         throw 'Layout region "'+target+'" not supported.';
34846     }
34847     
34848     
34849 });
34850  /*
34851  * Based on:
34852  * Ext JS Library 1.1.1
34853  * Copyright(c) 2006-2007, Ext JS, LLC.
34854  *
34855  * Originally Released Under LGPL - original licence link has changed is not relivant.
34856  *
34857  * Fork - LGPL
34858  * <script type="text/javascript">
34859  */
34860  
34861 /**
34862  * @class Roo.bootstrap.layout.Basic
34863  * @extends Roo.util.Observable
34864  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34865  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34866  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34867  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34868  * @cfg {string}   region  the region that it inhabits..
34869  * @cfg {bool}   skipConfig skip config?
34870  * 
34871
34872  */
34873 Roo.bootstrap.layout.Basic = function(config){
34874     
34875     this.mgr = config.mgr;
34876     
34877     this.position = config.region;
34878     
34879     var skipConfig = config.skipConfig;
34880     
34881     this.events = {
34882         /**
34883          * @scope Roo.BasicLayoutRegion
34884          */
34885         
34886         /**
34887          * @event beforeremove
34888          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34889          * @param {Roo.LayoutRegion} this
34890          * @param {Roo.ContentPanel} panel The panel
34891          * @param {Object} e The cancel event object
34892          */
34893         "beforeremove" : true,
34894         /**
34895          * @event invalidated
34896          * Fires when the layout for this region is changed.
34897          * @param {Roo.LayoutRegion} this
34898          */
34899         "invalidated" : true,
34900         /**
34901          * @event visibilitychange
34902          * Fires when this region is shown or hidden 
34903          * @param {Roo.LayoutRegion} this
34904          * @param {Boolean} visibility true or false
34905          */
34906         "visibilitychange" : true,
34907         /**
34908          * @event paneladded
34909          * Fires when a panel is added. 
34910          * @param {Roo.LayoutRegion} this
34911          * @param {Roo.ContentPanel} panel The panel
34912          */
34913         "paneladded" : true,
34914         /**
34915          * @event panelremoved
34916          * Fires when a panel is removed. 
34917          * @param {Roo.LayoutRegion} this
34918          * @param {Roo.ContentPanel} panel The panel
34919          */
34920         "panelremoved" : true,
34921         /**
34922          * @event beforecollapse
34923          * Fires when this region before collapse.
34924          * @param {Roo.LayoutRegion} this
34925          */
34926         "beforecollapse" : true,
34927         /**
34928          * @event collapsed
34929          * Fires when this region is collapsed.
34930          * @param {Roo.LayoutRegion} this
34931          */
34932         "collapsed" : true,
34933         /**
34934          * @event expanded
34935          * Fires when this region is expanded.
34936          * @param {Roo.LayoutRegion} this
34937          */
34938         "expanded" : true,
34939         /**
34940          * @event slideshow
34941          * Fires when this region is slid into view.
34942          * @param {Roo.LayoutRegion} this
34943          */
34944         "slideshow" : true,
34945         /**
34946          * @event slidehide
34947          * Fires when this region slides out of view. 
34948          * @param {Roo.LayoutRegion} this
34949          */
34950         "slidehide" : true,
34951         /**
34952          * @event panelactivated
34953          * Fires when a panel is activated. 
34954          * @param {Roo.LayoutRegion} this
34955          * @param {Roo.ContentPanel} panel The activated panel
34956          */
34957         "panelactivated" : true,
34958         /**
34959          * @event resized
34960          * Fires when the user resizes this region. 
34961          * @param {Roo.LayoutRegion} this
34962          * @param {Number} newSize The new size (width for east/west, height for north/south)
34963          */
34964         "resized" : true
34965     };
34966     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34967     this.panels = new Roo.util.MixedCollection();
34968     this.panels.getKey = this.getPanelId.createDelegate(this);
34969     this.box = null;
34970     this.activePanel = null;
34971     // ensure listeners are added...
34972     
34973     if (config.listeners || config.events) {
34974         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34975             listeners : config.listeners || {},
34976             events : config.events || {}
34977         });
34978     }
34979     
34980     if(skipConfig !== true){
34981         this.applyConfig(config);
34982     }
34983 };
34984
34985 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34986 {
34987     getPanelId : function(p){
34988         return p.getId();
34989     },
34990     
34991     applyConfig : function(config){
34992         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34993         this.config = config;
34994         
34995     },
34996     
34997     /**
34998      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34999      * the width, for horizontal (north, south) the height.
35000      * @param {Number} newSize The new width or height
35001      */
35002     resizeTo : function(newSize){
35003         var el = this.el ? this.el :
35004                  (this.activePanel ? this.activePanel.getEl() : null);
35005         if(el){
35006             switch(this.position){
35007                 case "east":
35008                 case "west":
35009                     el.setWidth(newSize);
35010                     this.fireEvent("resized", this, newSize);
35011                 break;
35012                 case "north":
35013                 case "south":
35014                     el.setHeight(newSize);
35015                     this.fireEvent("resized", this, newSize);
35016                 break;                
35017             }
35018         }
35019     },
35020     
35021     getBox : function(){
35022         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35023     },
35024     
35025     getMargins : function(){
35026         return this.margins;
35027     },
35028     
35029     updateBox : function(box){
35030         this.box = box;
35031         var el = this.activePanel.getEl();
35032         el.dom.style.left = box.x + "px";
35033         el.dom.style.top = box.y + "px";
35034         this.activePanel.setSize(box.width, box.height);
35035     },
35036     
35037     /**
35038      * Returns the container element for this region.
35039      * @return {Roo.Element}
35040      */
35041     getEl : function(){
35042         return this.activePanel;
35043     },
35044     
35045     /**
35046      * Returns true if this region is currently visible.
35047      * @return {Boolean}
35048      */
35049     isVisible : function(){
35050         return this.activePanel ? true : false;
35051     },
35052     
35053     setActivePanel : function(panel){
35054         panel = this.getPanel(panel);
35055         if(this.activePanel && this.activePanel != panel){
35056             this.activePanel.setActiveState(false);
35057             this.activePanel.getEl().setLeftTop(-10000,-10000);
35058         }
35059         this.activePanel = panel;
35060         panel.setActiveState(true);
35061         if(this.box){
35062             panel.setSize(this.box.width, this.box.height);
35063         }
35064         this.fireEvent("panelactivated", this, panel);
35065         this.fireEvent("invalidated");
35066     },
35067     
35068     /**
35069      * Show the specified panel.
35070      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35071      * @return {Roo.ContentPanel} The shown panel or null
35072      */
35073     showPanel : function(panel){
35074         panel = this.getPanel(panel);
35075         if(panel){
35076             this.setActivePanel(panel);
35077         }
35078         return panel;
35079     },
35080     
35081     /**
35082      * Get the active panel for this region.
35083      * @return {Roo.ContentPanel} The active panel or null
35084      */
35085     getActivePanel : function(){
35086         return this.activePanel;
35087     },
35088     
35089     /**
35090      * Add the passed ContentPanel(s)
35091      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35092      * @return {Roo.ContentPanel} The panel added (if only one was added)
35093      */
35094     add : function(panel){
35095         if(arguments.length > 1){
35096             for(var i = 0, len = arguments.length; i < len; i++) {
35097                 this.add(arguments[i]);
35098             }
35099             return null;
35100         }
35101         if(this.hasPanel(panel)){
35102             this.showPanel(panel);
35103             return panel;
35104         }
35105         var el = panel.getEl();
35106         if(el.dom.parentNode != this.mgr.el.dom){
35107             this.mgr.el.dom.appendChild(el.dom);
35108         }
35109         if(panel.setRegion){
35110             panel.setRegion(this);
35111         }
35112         this.panels.add(panel);
35113         el.setStyle("position", "absolute");
35114         if(!panel.background){
35115             this.setActivePanel(panel);
35116             if(this.config.initialSize && this.panels.getCount()==1){
35117                 this.resizeTo(this.config.initialSize);
35118             }
35119         }
35120         this.fireEvent("paneladded", this, panel);
35121         return panel;
35122     },
35123     
35124     /**
35125      * Returns true if the panel is in this region.
35126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35127      * @return {Boolean}
35128      */
35129     hasPanel : function(panel){
35130         if(typeof panel == "object"){ // must be panel obj
35131             panel = panel.getId();
35132         }
35133         return this.getPanel(panel) ? true : false;
35134     },
35135     
35136     /**
35137      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35139      * @param {Boolean} preservePanel Overrides the config preservePanel option
35140      * @return {Roo.ContentPanel} The panel that was removed
35141      */
35142     remove : function(panel, preservePanel){
35143         panel = this.getPanel(panel);
35144         if(!panel){
35145             return null;
35146         }
35147         var e = {};
35148         this.fireEvent("beforeremove", this, panel, e);
35149         if(e.cancel === true){
35150             return null;
35151         }
35152         var panelId = panel.getId();
35153         this.panels.removeKey(panelId);
35154         return panel;
35155     },
35156     
35157     /**
35158      * Returns the panel specified or null if it's not in this region.
35159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35160      * @return {Roo.ContentPanel}
35161      */
35162     getPanel : function(id){
35163         if(typeof id == "object"){ // must be panel obj
35164             return id;
35165         }
35166         return this.panels.get(id);
35167     },
35168     
35169     /**
35170      * Returns this regions position (north/south/east/west/center).
35171      * @return {String} 
35172      */
35173     getPosition: function(){
35174         return this.position;    
35175     }
35176 });/*
35177  * Based on:
35178  * Ext JS Library 1.1.1
35179  * Copyright(c) 2006-2007, Ext JS, LLC.
35180  *
35181  * Originally Released Under LGPL - original licence link has changed is not relivant.
35182  *
35183  * Fork - LGPL
35184  * <script type="text/javascript">
35185  */
35186  
35187 /**
35188  * @class Roo.bootstrap.layout.Region
35189  * @extends Roo.bootstrap.layout.Basic
35190  * This class represents a region in a layout manager.
35191  
35192  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35193  * @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})
35194  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35195  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35196  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35197  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35198  * @cfg {String}    title           The title for the region (overrides panel titles)
35199  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35200  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35201  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35202  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35203  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35204  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35205  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35206  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35207  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35208  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35209
35210  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35211  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35212  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35213  * @cfg {Number}    width           For East/West panels
35214  * @cfg {Number}    height          For North/South panels
35215  * @cfg {Boolean}   split           To show the splitter
35216  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35217  * 
35218  * @cfg {string}   cls             Extra CSS classes to add to region
35219  * 
35220  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35221  * @cfg {string}   region  the region that it inhabits..
35222  *
35223
35224  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35225  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35226
35227  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35228  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35229  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35230  */
35231 Roo.bootstrap.layout.Region = function(config)
35232 {
35233     this.applyConfig(config);
35234
35235     var mgr = config.mgr;
35236     var pos = config.region;
35237     config.skipConfig = true;
35238     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35239     
35240     if (mgr.el) {
35241         this.onRender(mgr.el);   
35242     }
35243      
35244     this.visible = true;
35245     this.collapsed = false;
35246     this.unrendered_panels = [];
35247 };
35248
35249 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35250
35251     position: '', // set by wrapper (eg. north/south etc..)
35252     unrendered_panels : null,  // unrendered panels.
35253     createBody : function(){
35254         /** This region's body element 
35255         * @type Roo.Element */
35256         this.bodyEl = this.el.createChild({
35257                 tag: "div",
35258                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35259         });
35260     },
35261
35262     onRender: function(ctr, pos)
35263     {
35264         var dh = Roo.DomHelper;
35265         /** This region's container element 
35266         * @type Roo.Element */
35267         this.el = dh.append(ctr.dom, {
35268                 tag: "div",
35269                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35270             }, true);
35271         /** This region's title element 
35272         * @type Roo.Element */
35273     
35274         this.titleEl = dh.append(this.el.dom,
35275             {
35276                     tag: "div",
35277                     unselectable: "on",
35278                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35279                     children:[
35280                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35281                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35282                     ]}, true);
35283         
35284         this.titleEl.enableDisplayMode();
35285         /** This region's title text element 
35286         * @type HTMLElement */
35287         this.titleTextEl = this.titleEl.dom.firstChild;
35288         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35289         /*
35290         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35291         this.closeBtn.enableDisplayMode();
35292         this.closeBtn.on("click", this.closeClicked, this);
35293         this.closeBtn.hide();
35294     */
35295         this.createBody(this.config);
35296         if(this.config.hideWhenEmpty){
35297             this.hide();
35298             this.on("paneladded", this.validateVisibility, this);
35299             this.on("panelremoved", this.validateVisibility, this);
35300         }
35301         if(this.autoScroll){
35302             this.bodyEl.setStyle("overflow", "auto");
35303         }else{
35304             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35305         }
35306         //if(c.titlebar !== false){
35307             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35308                 this.titleEl.hide();
35309             }else{
35310                 this.titleEl.show();
35311                 if(this.config.title){
35312                     this.titleTextEl.innerHTML = this.config.title;
35313                 }
35314             }
35315         //}
35316         if(this.config.collapsed){
35317             this.collapse(true);
35318         }
35319         if(this.config.hidden){
35320             this.hide();
35321         }
35322         
35323         if (this.unrendered_panels && this.unrendered_panels.length) {
35324             for (var i =0;i< this.unrendered_panels.length; i++) {
35325                 this.add(this.unrendered_panels[i]);
35326             }
35327             this.unrendered_panels = null;
35328             
35329         }
35330         
35331     },
35332     
35333     applyConfig : function(c)
35334     {
35335         /*
35336          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35337             var dh = Roo.DomHelper;
35338             if(c.titlebar !== false){
35339                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35340                 this.collapseBtn.on("click", this.collapse, this);
35341                 this.collapseBtn.enableDisplayMode();
35342                 /*
35343                 if(c.showPin === true || this.showPin){
35344                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35345                     this.stickBtn.enableDisplayMode();
35346                     this.stickBtn.on("click", this.expand, this);
35347                     this.stickBtn.hide();
35348                 }
35349                 
35350             }
35351             */
35352             /** This region's collapsed element
35353             * @type Roo.Element */
35354             /*
35355              *
35356             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35357                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35358             ]}, true);
35359             
35360             if(c.floatable !== false){
35361                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35362                this.collapsedEl.on("click", this.collapseClick, this);
35363             }
35364
35365             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35366                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35367                    id: "message", unselectable: "on", style:{"float":"left"}});
35368                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35369              }
35370             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35371             this.expandBtn.on("click", this.expand, this);
35372             
35373         }
35374         
35375         if(this.collapseBtn){
35376             this.collapseBtn.setVisible(c.collapsible == true);
35377         }
35378         
35379         this.cmargins = c.cmargins || this.cmargins ||
35380                          (this.position == "west" || this.position == "east" ?
35381                              {top: 0, left: 2, right:2, bottom: 0} :
35382                              {top: 2, left: 0, right:0, bottom: 2});
35383         */
35384         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35385         
35386         
35387         this.bottomTabs = c.tabPosition != "top";
35388         
35389         this.autoScroll = c.autoScroll || false;
35390         
35391         
35392        
35393         
35394         this.duration = c.duration || .30;
35395         this.slideDuration = c.slideDuration || .45;
35396         this.config = c;
35397        
35398     },
35399     /**
35400      * Returns true if this region is currently visible.
35401      * @return {Boolean}
35402      */
35403     isVisible : function(){
35404         return this.visible;
35405     },
35406
35407     /**
35408      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35409      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35410      */
35411     //setCollapsedTitle : function(title){
35412     //    title = title || "&#160;";
35413      //   if(this.collapsedTitleTextEl){
35414       //      this.collapsedTitleTextEl.innerHTML = title;
35415        // }
35416     //},
35417
35418     getBox : function(){
35419         var b;
35420       //  if(!this.collapsed){
35421             b = this.el.getBox(false, true);
35422        // }else{
35423           //  b = this.collapsedEl.getBox(false, true);
35424         //}
35425         return b;
35426     },
35427
35428     getMargins : function(){
35429         return this.margins;
35430         //return this.collapsed ? this.cmargins : this.margins;
35431     },
35432 /*
35433     highlight : function(){
35434         this.el.addClass("x-layout-panel-dragover");
35435     },
35436
35437     unhighlight : function(){
35438         this.el.removeClass("x-layout-panel-dragover");
35439     },
35440 */
35441     updateBox : function(box)
35442     {
35443         if (!this.bodyEl) {
35444             return; // not rendered yet..
35445         }
35446         
35447         this.box = box;
35448         if(!this.collapsed){
35449             this.el.dom.style.left = box.x + "px";
35450             this.el.dom.style.top = box.y + "px";
35451             this.updateBody(box.width, box.height);
35452         }else{
35453             this.collapsedEl.dom.style.left = box.x + "px";
35454             this.collapsedEl.dom.style.top = box.y + "px";
35455             this.collapsedEl.setSize(box.width, box.height);
35456         }
35457         if(this.tabs){
35458             this.tabs.autoSizeTabs();
35459         }
35460     },
35461
35462     updateBody : function(w, h)
35463     {
35464         if(w !== null){
35465             this.el.setWidth(w);
35466             w -= this.el.getBorderWidth("rl");
35467             if(this.config.adjustments){
35468                 w += this.config.adjustments[0];
35469             }
35470         }
35471         if(h !== null && h > 0){
35472             this.el.setHeight(h);
35473             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35474             h -= this.el.getBorderWidth("tb");
35475             if(this.config.adjustments){
35476                 h += this.config.adjustments[1];
35477             }
35478             this.bodyEl.setHeight(h);
35479             if(this.tabs){
35480                 h = this.tabs.syncHeight(h);
35481             }
35482         }
35483         if(this.panelSize){
35484             w = w !== null ? w : this.panelSize.width;
35485             h = h !== null ? h : this.panelSize.height;
35486         }
35487         if(this.activePanel){
35488             var el = this.activePanel.getEl();
35489             w = w !== null ? w : el.getWidth();
35490             h = h !== null ? h : el.getHeight();
35491             this.panelSize = {width: w, height: h};
35492             this.activePanel.setSize(w, h);
35493         }
35494         if(Roo.isIE && this.tabs){
35495             this.tabs.el.repaint();
35496         }
35497     },
35498
35499     /**
35500      * Returns the container element for this region.
35501      * @return {Roo.Element}
35502      */
35503     getEl : function(){
35504         return this.el;
35505     },
35506
35507     /**
35508      * Hides this region.
35509      */
35510     hide : function(){
35511         //if(!this.collapsed){
35512             this.el.dom.style.left = "-2000px";
35513             this.el.hide();
35514         //}else{
35515          //   this.collapsedEl.dom.style.left = "-2000px";
35516          //   this.collapsedEl.hide();
35517        // }
35518         this.visible = false;
35519         this.fireEvent("visibilitychange", this, false);
35520     },
35521
35522     /**
35523      * Shows this region if it was previously hidden.
35524      */
35525     show : function(){
35526         //if(!this.collapsed){
35527             this.el.show();
35528         //}else{
35529         //    this.collapsedEl.show();
35530        // }
35531         this.visible = true;
35532         this.fireEvent("visibilitychange", this, true);
35533     },
35534 /*
35535     closeClicked : function(){
35536         if(this.activePanel){
35537             this.remove(this.activePanel);
35538         }
35539     },
35540
35541     collapseClick : function(e){
35542         if(this.isSlid){
35543            e.stopPropagation();
35544            this.slideIn();
35545         }else{
35546            e.stopPropagation();
35547            this.slideOut();
35548         }
35549     },
35550 */
35551     /**
35552      * Collapses this region.
35553      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35554      */
35555     /*
35556     collapse : function(skipAnim, skipCheck = false){
35557         if(this.collapsed) {
35558             return;
35559         }
35560         
35561         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35562             
35563             this.collapsed = true;
35564             if(this.split){
35565                 this.split.el.hide();
35566             }
35567             if(this.config.animate && skipAnim !== true){
35568                 this.fireEvent("invalidated", this);
35569                 this.animateCollapse();
35570             }else{
35571                 this.el.setLocation(-20000,-20000);
35572                 this.el.hide();
35573                 this.collapsedEl.show();
35574                 this.fireEvent("collapsed", this);
35575                 this.fireEvent("invalidated", this);
35576             }
35577         }
35578         
35579     },
35580 */
35581     animateCollapse : function(){
35582         // overridden
35583     },
35584
35585     /**
35586      * Expands this region if it was previously collapsed.
35587      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35588      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35589      */
35590     /*
35591     expand : function(e, skipAnim){
35592         if(e) {
35593             e.stopPropagation();
35594         }
35595         if(!this.collapsed || this.el.hasActiveFx()) {
35596             return;
35597         }
35598         if(this.isSlid){
35599             this.afterSlideIn();
35600             skipAnim = true;
35601         }
35602         this.collapsed = false;
35603         if(this.config.animate && skipAnim !== true){
35604             this.animateExpand();
35605         }else{
35606             this.el.show();
35607             if(this.split){
35608                 this.split.el.show();
35609             }
35610             this.collapsedEl.setLocation(-2000,-2000);
35611             this.collapsedEl.hide();
35612             this.fireEvent("invalidated", this);
35613             this.fireEvent("expanded", this);
35614         }
35615     },
35616 */
35617     animateExpand : function(){
35618         // overridden
35619     },
35620
35621     initTabs : function()
35622     {
35623         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35624         
35625         var ts = new Roo.bootstrap.panel.Tabs({
35626                 el: this.bodyEl.dom,
35627                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35628                 disableTooltips: this.config.disableTabTips,
35629                 toolbar : this.config.toolbar
35630             });
35631         
35632         if(this.config.hideTabs){
35633             ts.stripWrap.setDisplayed(false);
35634         }
35635         this.tabs = ts;
35636         ts.resizeTabs = this.config.resizeTabs === true;
35637         ts.minTabWidth = this.config.minTabWidth || 40;
35638         ts.maxTabWidth = this.config.maxTabWidth || 250;
35639         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35640         ts.monitorResize = false;
35641         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35642         ts.bodyEl.addClass('roo-layout-tabs-body');
35643         this.panels.each(this.initPanelAsTab, this);
35644     },
35645
35646     initPanelAsTab : function(panel){
35647         var ti = this.tabs.addTab(
35648             panel.getEl().id,
35649             panel.getTitle(),
35650             null,
35651             this.config.closeOnTab && panel.isClosable(),
35652             panel.tpl
35653         );
35654         if(panel.tabTip !== undefined){
35655             ti.setTooltip(panel.tabTip);
35656         }
35657         ti.on("activate", function(){
35658               this.setActivePanel(panel);
35659         }, this);
35660         
35661         if(this.config.closeOnTab){
35662             ti.on("beforeclose", function(t, e){
35663                 e.cancel = true;
35664                 this.remove(panel);
35665             }, this);
35666         }
35667         
35668         panel.tabItem = ti;
35669         
35670         return ti;
35671     },
35672
35673     updatePanelTitle : function(panel, title)
35674     {
35675         if(this.activePanel == panel){
35676             this.updateTitle(title);
35677         }
35678         if(this.tabs){
35679             var ti = this.tabs.getTab(panel.getEl().id);
35680             ti.setText(title);
35681             if(panel.tabTip !== undefined){
35682                 ti.setTooltip(panel.tabTip);
35683             }
35684         }
35685     },
35686
35687     updateTitle : function(title){
35688         if(this.titleTextEl && !this.config.title){
35689             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35690         }
35691     },
35692
35693     setActivePanel : function(panel)
35694     {
35695         panel = this.getPanel(panel);
35696         if(this.activePanel && this.activePanel != panel){
35697             if(this.activePanel.setActiveState(false) === false){
35698                 return;
35699             }
35700         }
35701         this.activePanel = panel;
35702         panel.setActiveState(true);
35703         if(this.panelSize){
35704             panel.setSize(this.panelSize.width, this.panelSize.height);
35705         }
35706         if(this.closeBtn){
35707             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35708         }
35709         this.updateTitle(panel.getTitle());
35710         if(this.tabs){
35711             this.fireEvent("invalidated", this);
35712         }
35713         this.fireEvent("panelactivated", this, panel);
35714     },
35715
35716     /**
35717      * Shows the specified panel.
35718      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35719      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35720      */
35721     showPanel : function(panel)
35722     {
35723         panel = this.getPanel(panel);
35724         if(panel){
35725             if(this.tabs){
35726                 var tab = this.tabs.getTab(panel.getEl().id);
35727                 if(tab.isHidden()){
35728                     this.tabs.unhideTab(tab.id);
35729                 }
35730                 tab.activate();
35731             }else{
35732                 this.setActivePanel(panel);
35733             }
35734         }
35735         return panel;
35736     },
35737
35738     /**
35739      * Get the active panel for this region.
35740      * @return {Roo.ContentPanel} The active panel or null
35741      */
35742     getActivePanel : function(){
35743         return this.activePanel;
35744     },
35745
35746     validateVisibility : function(){
35747         if(this.panels.getCount() < 1){
35748             this.updateTitle("&#160;");
35749             this.closeBtn.hide();
35750             this.hide();
35751         }else{
35752             if(!this.isVisible()){
35753                 this.show();
35754             }
35755         }
35756     },
35757
35758     /**
35759      * Adds the passed ContentPanel(s) to this region.
35760      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35761      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35762      */
35763     add : function(panel)
35764     {
35765         if(arguments.length > 1){
35766             for(var i = 0, len = arguments.length; i < len; i++) {
35767                 this.add(arguments[i]);
35768             }
35769             return null;
35770         }
35771         
35772         // if we have not been rendered yet, then we can not really do much of this..
35773         if (!this.bodyEl) {
35774             this.unrendered_panels.push(panel);
35775             return panel;
35776         }
35777         
35778         
35779         
35780         
35781         if(this.hasPanel(panel)){
35782             this.showPanel(panel);
35783             return panel;
35784         }
35785         panel.setRegion(this);
35786         this.panels.add(panel);
35787        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35788             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35789             // and hide them... ???
35790             this.bodyEl.dom.appendChild(panel.getEl().dom);
35791             if(panel.background !== true){
35792                 this.setActivePanel(panel);
35793             }
35794             this.fireEvent("paneladded", this, panel);
35795             return panel;
35796         }
35797         */
35798         if(!this.tabs){
35799             this.initTabs();
35800         }else{
35801             this.initPanelAsTab(panel);
35802         }
35803         
35804         
35805         if(panel.background !== true){
35806             this.tabs.activate(panel.getEl().id);
35807         }
35808         this.fireEvent("paneladded", this, panel);
35809         return panel;
35810     },
35811
35812     /**
35813      * Hides the tab for the specified panel.
35814      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35815      */
35816     hidePanel : function(panel){
35817         if(this.tabs && (panel = this.getPanel(panel))){
35818             this.tabs.hideTab(panel.getEl().id);
35819         }
35820     },
35821
35822     /**
35823      * Unhides the tab for a previously hidden panel.
35824      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35825      */
35826     unhidePanel : function(panel){
35827         if(this.tabs && (panel = this.getPanel(panel))){
35828             this.tabs.unhideTab(panel.getEl().id);
35829         }
35830     },
35831
35832     clearPanels : function(){
35833         while(this.panels.getCount() > 0){
35834              this.remove(this.panels.first());
35835         }
35836     },
35837
35838     /**
35839      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35840      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35841      * @param {Boolean} preservePanel Overrides the config preservePanel option
35842      * @return {Roo.ContentPanel} The panel that was removed
35843      */
35844     remove : function(panel, preservePanel)
35845     {
35846         panel = this.getPanel(panel);
35847         if(!panel){
35848             return null;
35849         }
35850         var e = {};
35851         this.fireEvent("beforeremove", this, panel, e);
35852         if(e.cancel === true){
35853             return null;
35854         }
35855         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35856         var panelId = panel.getId();
35857         this.panels.removeKey(panelId);
35858         if(preservePanel){
35859             document.body.appendChild(panel.getEl().dom);
35860         }
35861         if(this.tabs){
35862             this.tabs.removeTab(panel.getEl().id);
35863         }else if (!preservePanel){
35864             this.bodyEl.dom.removeChild(panel.getEl().dom);
35865         }
35866         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35867             var p = this.panels.first();
35868             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35869             tempEl.appendChild(p.getEl().dom);
35870             this.bodyEl.update("");
35871             this.bodyEl.dom.appendChild(p.getEl().dom);
35872             tempEl = null;
35873             this.updateTitle(p.getTitle());
35874             this.tabs = null;
35875             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35876             this.setActivePanel(p);
35877         }
35878         panel.setRegion(null);
35879         if(this.activePanel == panel){
35880             this.activePanel = null;
35881         }
35882         if(this.config.autoDestroy !== false && preservePanel !== true){
35883             try{panel.destroy();}catch(e){}
35884         }
35885         this.fireEvent("panelremoved", this, panel);
35886         return panel;
35887     },
35888
35889     /**
35890      * Returns the TabPanel component used by this region
35891      * @return {Roo.TabPanel}
35892      */
35893     getTabs : function(){
35894         return this.tabs;
35895     },
35896
35897     createTool : function(parentEl, className){
35898         var btn = Roo.DomHelper.append(parentEl, {
35899             tag: "div",
35900             cls: "x-layout-tools-button",
35901             children: [ {
35902                 tag: "div",
35903                 cls: "roo-layout-tools-button-inner " + className,
35904                 html: "&#160;"
35905             }]
35906         }, true);
35907         btn.addClassOnOver("roo-layout-tools-button-over");
35908         return btn;
35909     }
35910 });/*
35911  * Based on:
35912  * Ext JS Library 1.1.1
35913  * Copyright(c) 2006-2007, Ext JS, LLC.
35914  *
35915  * Originally Released Under LGPL - original licence link has changed is not relivant.
35916  *
35917  * Fork - LGPL
35918  * <script type="text/javascript">
35919  */
35920  
35921
35922
35923 /**
35924  * @class Roo.SplitLayoutRegion
35925  * @extends Roo.LayoutRegion
35926  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35927  */
35928 Roo.bootstrap.layout.Split = function(config){
35929     this.cursor = config.cursor;
35930     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35931 };
35932
35933 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35934 {
35935     splitTip : "Drag to resize.",
35936     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35937     useSplitTips : false,
35938
35939     applyConfig : function(config){
35940         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35941     },
35942     
35943     onRender : function(ctr,pos) {
35944         
35945         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35946         if(!this.config.split){
35947             return;
35948         }
35949         if(!this.split){
35950             
35951             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35952                             tag: "div",
35953                             id: this.el.id + "-split",
35954                             cls: "roo-layout-split roo-layout-split-"+this.position,
35955                             html: "&#160;"
35956             });
35957             /** The SplitBar for this region 
35958             * @type Roo.SplitBar */
35959             // does not exist yet...
35960             Roo.log([this.position, this.orientation]);
35961             
35962             this.split = new Roo.bootstrap.SplitBar({
35963                 dragElement : splitEl,
35964                 resizingElement: this.el,
35965                 orientation : this.orientation
35966             });
35967             
35968             this.split.on("moved", this.onSplitMove, this);
35969             this.split.useShim = this.config.useShim === true;
35970             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35971             if(this.useSplitTips){
35972                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35973             }
35974             //if(config.collapsible){
35975             //    this.split.el.on("dblclick", this.collapse,  this);
35976             //}
35977         }
35978         if(typeof this.config.minSize != "undefined"){
35979             this.split.minSize = this.config.minSize;
35980         }
35981         if(typeof this.config.maxSize != "undefined"){
35982             this.split.maxSize = this.config.maxSize;
35983         }
35984         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35985             this.hideSplitter();
35986         }
35987         
35988     },
35989
35990     getHMaxSize : function(){
35991          var cmax = this.config.maxSize || 10000;
35992          var center = this.mgr.getRegion("center");
35993          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35994     },
35995
35996     getVMaxSize : function(){
35997          var cmax = this.config.maxSize || 10000;
35998          var center = this.mgr.getRegion("center");
35999          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36000     },
36001
36002     onSplitMove : function(split, newSize){
36003         this.fireEvent("resized", this, newSize);
36004     },
36005     
36006     /** 
36007      * Returns the {@link Roo.SplitBar} for this region.
36008      * @return {Roo.SplitBar}
36009      */
36010     getSplitBar : function(){
36011         return this.split;
36012     },
36013     
36014     hide : function(){
36015         this.hideSplitter();
36016         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36017     },
36018
36019     hideSplitter : function(){
36020         if(this.split){
36021             this.split.el.setLocation(-2000,-2000);
36022             this.split.el.hide();
36023         }
36024     },
36025
36026     show : function(){
36027         if(this.split){
36028             this.split.el.show();
36029         }
36030         Roo.bootstrap.layout.Split.superclass.show.call(this);
36031     },
36032     
36033     beforeSlide: function(){
36034         if(Roo.isGecko){// firefox overflow auto bug workaround
36035             this.bodyEl.clip();
36036             if(this.tabs) {
36037                 this.tabs.bodyEl.clip();
36038             }
36039             if(this.activePanel){
36040                 this.activePanel.getEl().clip();
36041                 
36042                 if(this.activePanel.beforeSlide){
36043                     this.activePanel.beforeSlide();
36044                 }
36045             }
36046         }
36047     },
36048     
36049     afterSlide : function(){
36050         if(Roo.isGecko){// firefox overflow auto bug workaround
36051             this.bodyEl.unclip();
36052             if(this.tabs) {
36053                 this.tabs.bodyEl.unclip();
36054             }
36055             if(this.activePanel){
36056                 this.activePanel.getEl().unclip();
36057                 if(this.activePanel.afterSlide){
36058                     this.activePanel.afterSlide();
36059                 }
36060             }
36061         }
36062     },
36063
36064     initAutoHide : function(){
36065         if(this.autoHide !== false){
36066             if(!this.autoHideHd){
36067                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36068                 this.autoHideHd = {
36069                     "mouseout": function(e){
36070                         if(!e.within(this.el, true)){
36071                             st.delay(500);
36072                         }
36073                     },
36074                     "mouseover" : function(e){
36075                         st.cancel();
36076                     },
36077                     scope : this
36078                 };
36079             }
36080             this.el.on(this.autoHideHd);
36081         }
36082     },
36083
36084     clearAutoHide : function(){
36085         if(this.autoHide !== false){
36086             this.el.un("mouseout", this.autoHideHd.mouseout);
36087             this.el.un("mouseover", this.autoHideHd.mouseover);
36088         }
36089     },
36090
36091     clearMonitor : function(){
36092         Roo.get(document).un("click", this.slideInIf, this);
36093     },
36094
36095     // these names are backwards but not changed for compat
36096     slideOut : function(){
36097         if(this.isSlid || this.el.hasActiveFx()){
36098             return;
36099         }
36100         this.isSlid = true;
36101         if(this.collapseBtn){
36102             this.collapseBtn.hide();
36103         }
36104         this.closeBtnState = this.closeBtn.getStyle('display');
36105         this.closeBtn.hide();
36106         if(this.stickBtn){
36107             this.stickBtn.show();
36108         }
36109         this.el.show();
36110         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36111         this.beforeSlide();
36112         this.el.setStyle("z-index", 10001);
36113         this.el.slideIn(this.getSlideAnchor(), {
36114             callback: function(){
36115                 this.afterSlide();
36116                 this.initAutoHide();
36117                 Roo.get(document).on("click", this.slideInIf, this);
36118                 this.fireEvent("slideshow", this);
36119             },
36120             scope: this,
36121             block: true
36122         });
36123     },
36124
36125     afterSlideIn : function(){
36126         this.clearAutoHide();
36127         this.isSlid = false;
36128         this.clearMonitor();
36129         this.el.setStyle("z-index", "");
36130         if(this.collapseBtn){
36131             this.collapseBtn.show();
36132         }
36133         this.closeBtn.setStyle('display', this.closeBtnState);
36134         if(this.stickBtn){
36135             this.stickBtn.hide();
36136         }
36137         this.fireEvent("slidehide", this);
36138     },
36139
36140     slideIn : function(cb){
36141         if(!this.isSlid || this.el.hasActiveFx()){
36142             Roo.callback(cb);
36143             return;
36144         }
36145         this.isSlid = false;
36146         this.beforeSlide();
36147         this.el.slideOut(this.getSlideAnchor(), {
36148             callback: function(){
36149                 this.el.setLeftTop(-10000, -10000);
36150                 this.afterSlide();
36151                 this.afterSlideIn();
36152                 Roo.callback(cb);
36153             },
36154             scope: this,
36155             block: true
36156         });
36157     },
36158     
36159     slideInIf : function(e){
36160         if(!e.within(this.el)){
36161             this.slideIn();
36162         }
36163     },
36164
36165     animateCollapse : function(){
36166         this.beforeSlide();
36167         this.el.setStyle("z-index", 20000);
36168         var anchor = this.getSlideAnchor();
36169         this.el.slideOut(anchor, {
36170             callback : function(){
36171                 this.el.setStyle("z-index", "");
36172                 this.collapsedEl.slideIn(anchor, {duration:.3});
36173                 this.afterSlide();
36174                 this.el.setLocation(-10000,-10000);
36175                 this.el.hide();
36176                 this.fireEvent("collapsed", this);
36177             },
36178             scope: this,
36179             block: true
36180         });
36181     },
36182
36183     animateExpand : function(){
36184         this.beforeSlide();
36185         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36186         this.el.setStyle("z-index", 20000);
36187         this.collapsedEl.hide({
36188             duration:.1
36189         });
36190         this.el.slideIn(this.getSlideAnchor(), {
36191             callback : function(){
36192                 this.el.setStyle("z-index", "");
36193                 this.afterSlide();
36194                 if(this.split){
36195                     this.split.el.show();
36196                 }
36197                 this.fireEvent("invalidated", this);
36198                 this.fireEvent("expanded", this);
36199             },
36200             scope: this,
36201             block: true
36202         });
36203     },
36204
36205     anchors : {
36206         "west" : "left",
36207         "east" : "right",
36208         "north" : "top",
36209         "south" : "bottom"
36210     },
36211
36212     sanchors : {
36213         "west" : "l",
36214         "east" : "r",
36215         "north" : "t",
36216         "south" : "b"
36217     },
36218
36219     canchors : {
36220         "west" : "tl-tr",
36221         "east" : "tr-tl",
36222         "north" : "tl-bl",
36223         "south" : "bl-tl"
36224     },
36225
36226     getAnchor : function(){
36227         return this.anchors[this.position];
36228     },
36229
36230     getCollapseAnchor : function(){
36231         return this.canchors[this.position];
36232     },
36233
36234     getSlideAnchor : function(){
36235         return this.sanchors[this.position];
36236     },
36237
36238     getAlignAdj : function(){
36239         var cm = this.cmargins;
36240         switch(this.position){
36241             case "west":
36242                 return [0, 0];
36243             break;
36244             case "east":
36245                 return [0, 0];
36246             break;
36247             case "north":
36248                 return [0, 0];
36249             break;
36250             case "south":
36251                 return [0, 0];
36252             break;
36253         }
36254     },
36255
36256     getExpandAdj : function(){
36257         var c = this.collapsedEl, cm = this.cmargins;
36258         switch(this.position){
36259             case "west":
36260                 return [-(cm.right+c.getWidth()+cm.left), 0];
36261             break;
36262             case "east":
36263                 return [cm.right+c.getWidth()+cm.left, 0];
36264             break;
36265             case "north":
36266                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36267             break;
36268             case "south":
36269                 return [0, cm.top+cm.bottom+c.getHeight()];
36270             break;
36271         }
36272     }
36273 });/*
36274  * Based on:
36275  * Ext JS Library 1.1.1
36276  * Copyright(c) 2006-2007, Ext JS, LLC.
36277  *
36278  * Originally Released Under LGPL - original licence link has changed is not relivant.
36279  *
36280  * Fork - LGPL
36281  * <script type="text/javascript">
36282  */
36283 /*
36284  * These classes are private internal classes
36285  */
36286 Roo.bootstrap.layout.Center = function(config){
36287     config.region = "center";
36288     Roo.bootstrap.layout.Region.call(this, config);
36289     this.visible = true;
36290     this.minWidth = config.minWidth || 20;
36291     this.minHeight = config.minHeight || 20;
36292 };
36293
36294 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36295     hide : function(){
36296         // center panel can't be hidden
36297     },
36298     
36299     show : function(){
36300         // center panel can't be hidden
36301     },
36302     
36303     getMinWidth: function(){
36304         return this.minWidth;
36305     },
36306     
36307     getMinHeight: function(){
36308         return this.minHeight;
36309     }
36310 });
36311
36312
36313
36314
36315  
36316
36317
36318
36319
36320
36321 Roo.bootstrap.layout.North = function(config)
36322 {
36323     config.region = 'north';
36324     config.cursor = 'n-resize';
36325     
36326     Roo.bootstrap.layout.Split.call(this, config);
36327     
36328     
36329     if(this.split){
36330         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36331         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36332         this.split.el.addClass("roo-layout-split-v");
36333     }
36334     var size = config.initialSize || config.height;
36335     if(typeof size != "undefined"){
36336         this.el.setHeight(size);
36337     }
36338 };
36339 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36340 {
36341     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36342     
36343     
36344     
36345     getBox : function(){
36346         if(this.collapsed){
36347             return this.collapsedEl.getBox();
36348         }
36349         var box = this.el.getBox();
36350         if(this.split){
36351             box.height += this.split.el.getHeight();
36352         }
36353         return box;
36354     },
36355     
36356     updateBox : function(box){
36357         if(this.split && !this.collapsed){
36358             box.height -= this.split.el.getHeight();
36359             this.split.el.setLeft(box.x);
36360             this.split.el.setTop(box.y+box.height);
36361             this.split.el.setWidth(box.width);
36362         }
36363         if(this.collapsed){
36364             this.updateBody(box.width, null);
36365         }
36366         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36367     }
36368 });
36369
36370
36371
36372
36373
36374 Roo.bootstrap.layout.South = function(config){
36375     config.region = 'south';
36376     config.cursor = 's-resize';
36377     Roo.bootstrap.layout.Split.call(this, config);
36378     if(this.split){
36379         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36380         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36381         this.split.el.addClass("roo-layout-split-v");
36382     }
36383     var size = config.initialSize || config.height;
36384     if(typeof size != "undefined"){
36385         this.el.setHeight(size);
36386     }
36387 };
36388
36389 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36390     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36391     getBox : function(){
36392         if(this.collapsed){
36393             return this.collapsedEl.getBox();
36394         }
36395         var box = this.el.getBox();
36396         if(this.split){
36397             var sh = this.split.el.getHeight();
36398             box.height += sh;
36399             box.y -= sh;
36400         }
36401         return box;
36402     },
36403     
36404     updateBox : function(box){
36405         if(this.split && !this.collapsed){
36406             var sh = this.split.el.getHeight();
36407             box.height -= sh;
36408             box.y += sh;
36409             this.split.el.setLeft(box.x);
36410             this.split.el.setTop(box.y-sh);
36411             this.split.el.setWidth(box.width);
36412         }
36413         if(this.collapsed){
36414             this.updateBody(box.width, null);
36415         }
36416         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36417     }
36418 });
36419
36420 Roo.bootstrap.layout.East = function(config){
36421     config.region = "east";
36422     config.cursor = "e-resize";
36423     Roo.bootstrap.layout.Split.call(this, config);
36424     if(this.split){
36425         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36426         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36427         this.split.el.addClass("roo-layout-split-h");
36428     }
36429     var size = config.initialSize || config.width;
36430     if(typeof size != "undefined"){
36431         this.el.setWidth(size);
36432     }
36433 };
36434 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36435     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36436     getBox : function(){
36437         if(this.collapsed){
36438             return this.collapsedEl.getBox();
36439         }
36440         var box = this.el.getBox();
36441         if(this.split){
36442             var sw = this.split.el.getWidth();
36443             box.width += sw;
36444             box.x -= sw;
36445         }
36446         return box;
36447     },
36448
36449     updateBox : function(box){
36450         if(this.split && !this.collapsed){
36451             var sw = this.split.el.getWidth();
36452             box.width -= sw;
36453             this.split.el.setLeft(box.x);
36454             this.split.el.setTop(box.y);
36455             this.split.el.setHeight(box.height);
36456             box.x += sw;
36457         }
36458         if(this.collapsed){
36459             this.updateBody(null, box.height);
36460         }
36461         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36462     }
36463 });
36464
36465 Roo.bootstrap.layout.West = function(config){
36466     config.region = "west";
36467     config.cursor = "w-resize";
36468     
36469     Roo.bootstrap.layout.Split.call(this, config);
36470     if(this.split){
36471         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36472         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36473         this.split.el.addClass("roo-layout-split-h");
36474     }
36475     
36476 };
36477 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36478     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36479     
36480     onRender: function(ctr, pos)
36481     {
36482         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36483         var size = this.config.initialSize || this.config.width;
36484         if(typeof size != "undefined"){
36485             this.el.setWidth(size);
36486         }
36487     },
36488     
36489     getBox : function(){
36490         if(this.collapsed){
36491             return this.collapsedEl.getBox();
36492         }
36493         var box = this.el.getBox();
36494         if(this.split){
36495             box.width += this.split.el.getWidth();
36496         }
36497         return box;
36498     },
36499     
36500     updateBox : function(box){
36501         if(this.split && !this.collapsed){
36502             var sw = this.split.el.getWidth();
36503             box.width -= sw;
36504             this.split.el.setLeft(box.x+box.width);
36505             this.split.el.setTop(box.y);
36506             this.split.el.setHeight(box.height);
36507         }
36508         if(this.collapsed){
36509             this.updateBody(null, box.height);
36510         }
36511         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36512     }
36513 });
36514 Roo.namespace("Roo.bootstrap.panel");/*
36515  * Based on:
36516  * Ext JS Library 1.1.1
36517  * Copyright(c) 2006-2007, Ext JS, LLC.
36518  *
36519  * Originally Released Under LGPL - original licence link has changed is not relivant.
36520  *
36521  * Fork - LGPL
36522  * <script type="text/javascript">
36523  */
36524 /**
36525  * @class Roo.ContentPanel
36526  * @extends Roo.util.Observable
36527  * A basic ContentPanel element.
36528  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36529  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36530  * @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
36531  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36532  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36533  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36534  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36535  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36536  * @cfg {String} title          The title for this panel
36537  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36538  * @cfg {String} url            Calls {@link #setUrl} with this value
36539  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36540  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36541  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36542  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36543  * @cfg {Boolean} badges render the badges
36544
36545  * @constructor
36546  * Create a new ContentPanel.
36547  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36548  * @param {String/Object} config A string to set only the title or a config object
36549  * @param {String} content (optional) Set the HTML content for this panel
36550  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36551  */
36552 Roo.bootstrap.panel.Content = function( config){
36553     
36554     this.tpl = config.tpl || false;
36555     
36556     var el = config.el;
36557     var content = config.content;
36558
36559     if(config.autoCreate){ // xtype is available if this is called from factory
36560         el = Roo.id();
36561     }
36562     this.el = Roo.get(el);
36563     if(!this.el && config && config.autoCreate){
36564         if(typeof config.autoCreate == "object"){
36565             if(!config.autoCreate.id){
36566                 config.autoCreate.id = config.id||el;
36567             }
36568             this.el = Roo.DomHelper.append(document.body,
36569                         config.autoCreate, true);
36570         }else{
36571             var elcfg =  {   tag: "div",
36572                             cls: "roo-layout-inactive-content",
36573                             id: config.id||el
36574                             };
36575             if (config.html) {
36576                 elcfg.html = config.html;
36577                 
36578             }
36579                         
36580             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36581         }
36582     } 
36583     this.closable = false;
36584     this.loaded = false;
36585     this.active = false;
36586    
36587       
36588     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36589         
36590         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36591         
36592         this.wrapEl = this.el; //this.el.wrap();
36593         var ti = [];
36594         if (config.toolbar.items) {
36595             ti = config.toolbar.items ;
36596             delete config.toolbar.items ;
36597         }
36598         
36599         var nitems = [];
36600         this.toolbar.render(this.wrapEl, 'before');
36601         for(var i =0;i < ti.length;i++) {
36602           //  Roo.log(['add child', items[i]]);
36603             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36604         }
36605         this.toolbar.items = nitems;
36606         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36607         delete config.toolbar;
36608         
36609     }
36610     /*
36611     // xtype created footer. - not sure if will work as we normally have to render first..
36612     if (this.footer && !this.footer.el && this.footer.xtype) {
36613         if (!this.wrapEl) {
36614             this.wrapEl = this.el.wrap();
36615         }
36616     
36617         this.footer.container = this.wrapEl.createChild();
36618          
36619         this.footer = Roo.factory(this.footer, Roo);
36620         
36621     }
36622     */
36623     
36624      if(typeof config == "string"){
36625         this.title = config;
36626     }else{
36627         Roo.apply(this, config);
36628     }
36629     
36630     if(this.resizeEl){
36631         this.resizeEl = Roo.get(this.resizeEl, true);
36632     }else{
36633         this.resizeEl = this.el;
36634     }
36635     // handle view.xtype
36636     
36637  
36638     
36639     
36640     this.addEvents({
36641         /**
36642          * @event activate
36643          * Fires when this panel is activated. 
36644          * @param {Roo.ContentPanel} this
36645          */
36646         "activate" : true,
36647         /**
36648          * @event deactivate
36649          * Fires when this panel is activated. 
36650          * @param {Roo.ContentPanel} this
36651          */
36652         "deactivate" : true,
36653
36654         /**
36655          * @event resize
36656          * Fires when this panel is resized if fitToFrame is true.
36657          * @param {Roo.ContentPanel} this
36658          * @param {Number} width The width after any component adjustments
36659          * @param {Number} height The height after any component adjustments
36660          */
36661         "resize" : true,
36662         
36663          /**
36664          * @event render
36665          * Fires when this tab is created
36666          * @param {Roo.ContentPanel} this
36667          */
36668         "render" : true
36669         
36670         
36671         
36672     });
36673     
36674
36675     
36676     
36677     if(this.autoScroll){
36678         this.resizeEl.setStyle("overflow", "auto");
36679     } else {
36680         // fix randome scrolling
36681         //this.el.on('scroll', function() {
36682         //    Roo.log('fix random scolling');
36683         //    this.scrollTo('top',0); 
36684         //});
36685     }
36686     content = content || this.content;
36687     if(content){
36688         this.setContent(content);
36689     }
36690     if(config && config.url){
36691         this.setUrl(this.url, this.params, this.loadOnce);
36692     }
36693     
36694     
36695     
36696     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36697     
36698     if (this.view && typeof(this.view.xtype) != 'undefined') {
36699         this.view.el = this.el.appendChild(document.createElement("div"));
36700         this.view = Roo.factory(this.view); 
36701         this.view.render  &&  this.view.render(false, '');  
36702     }
36703     
36704     
36705     this.fireEvent('render', this);
36706 };
36707
36708 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36709     
36710     tabTip : '',
36711     
36712     setRegion : function(region){
36713         this.region = region;
36714         this.setActiveClass(region && !this.background);
36715     },
36716     
36717     
36718     setActiveClass: function(state)
36719     {
36720         if(state){
36721            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36722            this.el.setStyle('position','relative');
36723         }else{
36724            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36725            this.el.setStyle('position', 'absolute');
36726         } 
36727     },
36728     
36729     /**
36730      * Returns the toolbar for this Panel if one was configured. 
36731      * @return {Roo.Toolbar} 
36732      */
36733     getToolbar : function(){
36734         return this.toolbar;
36735     },
36736     
36737     setActiveState : function(active)
36738     {
36739         this.active = active;
36740         this.setActiveClass(active);
36741         if(!active){
36742             if(this.fireEvent("deactivate", this) === false){
36743                 return false;
36744             }
36745             return true;
36746         }
36747         this.fireEvent("activate", this);
36748         return true;
36749     },
36750     /**
36751      * Updates this panel's element
36752      * @param {String} content The new content
36753      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36754     */
36755     setContent : function(content, loadScripts){
36756         this.el.update(content, loadScripts);
36757     },
36758
36759     ignoreResize : function(w, h){
36760         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36761             return true;
36762         }else{
36763             this.lastSize = {width: w, height: h};
36764             return false;
36765         }
36766     },
36767     /**
36768      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36769      * @return {Roo.UpdateManager} The UpdateManager
36770      */
36771     getUpdateManager : function(){
36772         return this.el.getUpdateManager();
36773     },
36774      /**
36775      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36776      * @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:
36777 <pre><code>
36778 panel.load({
36779     url: "your-url.php",
36780     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36781     callback: yourFunction,
36782     scope: yourObject, //(optional scope)
36783     discardUrl: false,
36784     nocache: false,
36785     text: "Loading...",
36786     timeout: 30,
36787     scripts: false
36788 });
36789 </code></pre>
36790      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36791      * 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.
36792      * @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}
36793      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36794      * @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.
36795      * @return {Roo.ContentPanel} this
36796      */
36797     load : function(){
36798         var um = this.el.getUpdateManager();
36799         um.update.apply(um, arguments);
36800         return this;
36801     },
36802
36803
36804     /**
36805      * 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.
36806      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36807      * @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)
36808      * @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)
36809      * @return {Roo.UpdateManager} The UpdateManager
36810      */
36811     setUrl : function(url, params, loadOnce){
36812         if(this.refreshDelegate){
36813             this.removeListener("activate", this.refreshDelegate);
36814         }
36815         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36816         this.on("activate", this.refreshDelegate);
36817         return this.el.getUpdateManager();
36818     },
36819     
36820     _handleRefresh : function(url, params, loadOnce){
36821         if(!loadOnce || !this.loaded){
36822             var updater = this.el.getUpdateManager();
36823             updater.update(url, params, this._setLoaded.createDelegate(this));
36824         }
36825     },
36826     
36827     _setLoaded : function(){
36828         this.loaded = true;
36829     }, 
36830     
36831     /**
36832      * Returns this panel's id
36833      * @return {String} 
36834      */
36835     getId : function(){
36836         return this.el.id;
36837     },
36838     
36839     /** 
36840      * Returns this panel's element - used by regiosn to add.
36841      * @return {Roo.Element} 
36842      */
36843     getEl : function(){
36844         return this.wrapEl || this.el;
36845     },
36846     
36847    
36848     
36849     adjustForComponents : function(width, height)
36850     {
36851         //Roo.log('adjustForComponents ');
36852         if(this.resizeEl != this.el){
36853             width -= this.el.getFrameWidth('lr');
36854             height -= this.el.getFrameWidth('tb');
36855         }
36856         if(this.toolbar){
36857             var te = this.toolbar.getEl();
36858             te.setWidth(width);
36859             height -= te.getHeight();
36860         }
36861         if(this.footer){
36862             var te = this.footer.getEl();
36863             te.setWidth(width);
36864             height -= te.getHeight();
36865         }
36866         
36867         
36868         if(this.adjustments){
36869             width += this.adjustments[0];
36870             height += this.adjustments[1];
36871         }
36872         return {"width": width, "height": height};
36873     },
36874     
36875     setSize : function(width, height){
36876         if(this.fitToFrame && !this.ignoreResize(width, height)){
36877             if(this.fitContainer && this.resizeEl != this.el){
36878                 this.el.setSize(width, height);
36879             }
36880             var size = this.adjustForComponents(width, height);
36881             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36882             this.fireEvent('resize', this, size.width, size.height);
36883         }
36884     },
36885     
36886     /**
36887      * Returns this panel's title
36888      * @return {String} 
36889      */
36890     getTitle : function(){
36891         
36892         if (typeof(this.title) != 'object') {
36893             return this.title;
36894         }
36895         
36896         var t = '';
36897         for (var k in this.title) {
36898             if (!this.title.hasOwnProperty(k)) {
36899                 continue;
36900             }
36901             
36902             if (k.indexOf('-') >= 0) {
36903                 var s = k.split('-');
36904                 for (var i = 0; i<s.length; i++) {
36905                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36906                 }
36907             } else {
36908                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36909             }
36910         }
36911         return t;
36912     },
36913     
36914     /**
36915      * Set this panel's title
36916      * @param {String} title
36917      */
36918     setTitle : function(title){
36919         this.title = title;
36920         if(this.region){
36921             this.region.updatePanelTitle(this, title);
36922         }
36923     },
36924     
36925     /**
36926      * Returns true is this panel was configured to be closable
36927      * @return {Boolean} 
36928      */
36929     isClosable : function(){
36930         return this.closable;
36931     },
36932     
36933     beforeSlide : function(){
36934         this.el.clip();
36935         this.resizeEl.clip();
36936     },
36937     
36938     afterSlide : function(){
36939         this.el.unclip();
36940         this.resizeEl.unclip();
36941     },
36942     
36943     /**
36944      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36945      *   Will fail silently if the {@link #setUrl} method has not been called.
36946      *   This does not activate the panel, just updates its content.
36947      */
36948     refresh : function(){
36949         if(this.refreshDelegate){
36950            this.loaded = false;
36951            this.refreshDelegate();
36952         }
36953     },
36954     
36955     /**
36956      * Destroys this panel
36957      */
36958     destroy : function(){
36959         this.el.removeAllListeners();
36960         var tempEl = document.createElement("span");
36961         tempEl.appendChild(this.el.dom);
36962         tempEl.innerHTML = "";
36963         this.el.remove();
36964         this.el = null;
36965     },
36966     
36967     /**
36968      * form - if the content panel contains a form - this is a reference to it.
36969      * @type {Roo.form.Form}
36970      */
36971     form : false,
36972     /**
36973      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36974      *    This contains a reference to it.
36975      * @type {Roo.View}
36976      */
36977     view : false,
36978     
36979       /**
36980      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36981      * <pre><code>
36982
36983 layout.addxtype({
36984        xtype : 'Form',
36985        items: [ .... ]
36986    }
36987 );
36988
36989 </code></pre>
36990      * @param {Object} cfg Xtype definition of item to add.
36991      */
36992     
36993     
36994     getChildContainer: function () {
36995         return this.getEl();
36996     }
36997     
36998     
36999     /*
37000         var  ret = new Roo.factory(cfg);
37001         return ret;
37002         
37003         
37004         // add form..
37005         if (cfg.xtype.match(/^Form$/)) {
37006             
37007             var el;
37008             //if (this.footer) {
37009             //    el = this.footer.container.insertSibling(false, 'before');
37010             //} else {
37011                 el = this.el.createChild();
37012             //}
37013
37014             this.form = new  Roo.form.Form(cfg);
37015             
37016             
37017             if ( this.form.allItems.length) {
37018                 this.form.render(el.dom);
37019             }
37020             return this.form;
37021         }
37022         // should only have one of theses..
37023         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37024             // views.. should not be just added - used named prop 'view''
37025             
37026             cfg.el = this.el.appendChild(document.createElement("div"));
37027             // factory?
37028             
37029             var ret = new Roo.factory(cfg);
37030              
37031              ret.render && ret.render(false, ''); // render blank..
37032             this.view = ret;
37033             return ret;
37034         }
37035         return false;
37036     }
37037     \*/
37038 });
37039  
37040 /**
37041  * @class Roo.bootstrap.panel.Grid
37042  * @extends Roo.bootstrap.panel.Content
37043  * @constructor
37044  * Create a new GridPanel.
37045  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37046  * @param {Object} config A the config object
37047   
37048  */
37049
37050
37051
37052 Roo.bootstrap.panel.Grid = function(config)
37053 {
37054     
37055       
37056     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37057         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37058
37059     config.el = this.wrapper;
37060     //this.el = this.wrapper;
37061     
37062       if (config.container) {
37063         // ctor'ed from a Border/panel.grid
37064         
37065         
37066         this.wrapper.setStyle("overflow", "hidden");
37067         this.wrapper.addClass('roo-grid-container');
37068
37069     }
37070     
37071     
37072     if(config.toolbar){
37073         var tool_el = this.wrapper.createChild();    
37074         this.toolbar = Roo.factory(config.toolbar);
37075         var ti = [];
37076         if (config.toolbar.items) {
37077             ti = config.toolbar.items ;
37078             delete config.toolbar.items ;
37079         }
37080         
37081         var nitems = [];
37082         this.toolbar.render(tool_el);
37083         for(var i =0;i < ti.length;i++) {
37084           //  Roo.log(['add child', items[i]]);
37085             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37086         }
37087         this.toolbar.items = nitems;
37088         
37089         delete config.toolbar;
37090     }
37091     
37092     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37093     config.grid.scrollBody = true;;
37094     config.grid.monitorWindowResize = false; // turn off autosizing
37095     config.grid.autoHeight = false;
37096     config.grid.autoWidth = false;
37097     
37098     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37099     
37100     if (config.background) {
37101         // render grid on panel activation (if panel background)
37102         this.on('activate', function(gp) {
37103             if (!gp.grid.rendered) {
37104                 gp.grid.render(this.wrapper);
37105                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37106             }
37107         });
37108             
37109     } else {
37110         this.grid.render(this.wrapper);
37111         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37112
37113     }
37114     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37115     // ??? needed ??? config.el = this.wrapper;
37116     
37117     
37118     
37119   
37120     // xtype created footer. - not sure if will work as we normally have to render first..
37121     if (this.footer && !this.footer.el && this.footer.xtype) {
37122         
37123         var ctr = this.grid.getView().getFooterPanel(true);
37124         this.footer.dataSource = this.grid.dataSource;
37125         this.footer = Roo.factory(this.footer, Roo);
37126         this.footer.render(ctr);
37127         
37128     }
37129     
37130     
37131     
37132     
37133      
37134 };
37135
37136 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37137     getId : function(){
37138         return this.grid.id;
37139     },
37140     
37141     /**
37142      * Returns the grid for this panel
37143      * @return {Roo.bootstrap.Table} 
37144      */
37145     getGrid : function(){
37146         return this.grid;    
37147     },
37148     
37149     setSize : function(width, height){
37150         if(!this.ignoreResize(width, height)){
37151             var grid = this.grid;
37152             var size = this.adjustForComponents(width, height);
37153             var gridel = grid.getGridEl();
37154             gridel.setSize(size.width, size.height);
37155             /*
37156             var thd = grid.getGridEl().select('thead',true).first();
37157             var tbd = grid.getGridEl().select('tbody', true).first();
37158             if (tbd) {
37159                 tbd.setSize(width, height - thd.getHeight());
37160             }
37161             */
37162             grid.autoSize();
37163         }
37164     },
37165      
37166     
37167     
37168     beforeSlide : function(){
37169         this.grid.getView().scroller.clip();
37170     },
37171     
37172     afterSlide : function(){
37173         this.grid.getView().scroller.unclip();
37174     },
37175     
37176     destroy : function(){
37177         this.grid.destroy();
37178         delete this.grid;
37179         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37180     }
37181 });
37182
37183 /**
37184  * @class Roo.bootstrap.panel.Nest
37185  * @extends Roo.bootstrap.panel.Content
37186  * @constructor
37187  * Create a new Panel, that can contain a layout.Border.
37188  * 
37189  * 
37190  * @param {Roo.BorderLayout} layout The layout for this panel
37191  * @param {String/Object} config A string to set only the title or a config object
37192  */
37193 Roo.bootstrap.panel.Nest = function(config)
37194 {
37195     // construct with only one argument..
37196     /* FIXME - implement nicer consturctors
37197     if (layout.layout) {
37198         config = layout;
37199         layout = config.layout;
37200         delete config.layout;
37201     }
37202     if (layout.xtype && !layout.getEl) {
37203         // then layout needs constructing..
37204         layout = Roo.factory(layout, Roo);
37205     }
37206     */
37207     
37208     config.el =  config.layout.getEl();
37209     
37210     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37211     
37212     config.layout.monitorWindowResize = false; // turn off autosizing
37213     this.layout = config.layout;
37214     this.layout.getEl().addClass("roo-layout-nested-layout");
37215     
37216     
37217     
37218     
37219 };
37220
37221 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37222
37223     setSize : function(width, height){
37224         if(!this.ignoreResize(width, height)){
37225             var size = this.adjustForComponents(width, height);
37226             var el = this.layout.getEl();
37227             if (size.height < 1) {
37228                 el.setWidth(size.width);   
37229             } else {
37230                 el.setSize(size.width, size.height);
37231             }
37232             var touch = el.dom.offsetWidth;
37233             this.layout.layout();
37234             // ie requires a double layout on the first pass
37235             if(Roo.isIE && !this.initialized){
37236                 this.initialized = true;
37237                 this.layout.layout();
37238             }
37239         }
37240     },
37241     
37242     // activate all subpanels if not currently active..
37243     
37244     setActiveState : function(active){
37245         this.active = active;
37246         this.setActiveClass(active);
37247         
37248         if(!active){
37249             this.fireEvent("deactivate", this);
37250             return;
37251         }
37252         
37253         this.fireEvent("activate", this);
37254         // not sure if this should happen before or after..
37255         if (!this.layout) {
37256             return; // should not happen..
37257         }
37258         var reg = false;
37259         for (var r in this.layout.regions) {
37260             reg = this.layout.getRegion(r);
37261             if (reg.getActivePanel()) {
37262                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37263                 reg.setActivePanel(reg.getActivePanel());
37264                 continue;
37265             }
37266             if (!reg.panels.length) {
37267                 continue;
37268             }
37269             reg.showPanel(reg.getPanel(0));
37270         }
37271         
37272         
37273         
37274         
37275     },
37276     
37277     /**
37278      * Returns the nested BorderLayout for this panel
37279      * @return {Roo.BorderLayout} 
37280      */
37281     getLayout : function(){
37282         return this.layout;
37283     },
37284     
37285      /**
37286      * Adds a xtype elements to the layout of the nested panel
37287      * <pre><code>
37288
37289 panel.addxtype({
37290        xtype : 'ContentPanel',
37291        region: 'west',
37292        items: [ .... ]
37293    }
37294 );
37295
37296 panel.addxtype({
37297         xtype : 'NestedLayoutPanel',
37298         region: 'west',
37299         layout: {
37300            center: { },
37301            west: { }   
37302         },
37303         items : [ ... list of content panels or nested layout panels.. ]
37304    }
37305 );
37306 </code></pre>
37307      * @param {Object} cfg Xtype definition of item to add.
37308      */
37309     addxtype : function(cfg) {
37310         return this.layout.addxtype(cfg);
37311     
37312     }
37313 });        /*
37314  * Based on:
37315  * Ext JS Library 1.1.1
37316  * Copyright(c) 2006-2007, Ext JS, LLC.
37317  *
37318  * Originally Released Under LGPL - original licence link has changed is not relivant.
37319  *
37320  * Fork - LGPL
37321  * <script type="text/javascript">
37322  */
37323 /**
37324  * @class Roo.TabPanel
37325  * @extends Roo.util.Observable
37326  * A lightweight tab container.
37327  * <br><br>
37328  * Usage:
37329  * <pre><code>
37330 // basic tabs 1, built from existing content
37331 var tabs = new Roo.TabPanel("tabs1");
37332 tabs.addTab("script", "View Script");
37333 tabs.addTab("markup", "View Markup");
37334 tabs.activate("script");
37335
37336 // more advanced tabs, built from javascript
37337 var jtabs = new Roo.TabPanel("jtabs");
37338 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37339
37340 // set up the UpdateManager
37341 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37342 var updater = tab2.getUpdateManager();
37343 updater.setDefaultUrl("ajax1.htm");
37344 tab2.on('activate', updater.refresh, updater, true);
37345
37346 // Use setUrl for Ajax loading
37347 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37348 tab3.setUrl("ajax2.htm", null, true);
37349
37350 // Disabled tab
37351 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37352 tab4.disable();
37353
37354 jtabs.activate("jtabs-1");
37355  * </code></pre>
37356  * @constructor
37357  * Create a new TabPanel.
37358  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37359  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37360  */
37361 Roo.bootstrap.panel.Tabs = function(config){
37362     /**
37363     * The container element for this TabPanel.
37364     * @type Roo.Element
37365     */
37366     this.el = Roo.get(config.el);
37367     delete config.el;
37368     if(config){
37369         if(typeof config == "boolean"){
37370             this.tabPosition = config ? "bottom" : "top";
37371         }else{
37372             Roo.apply(this, config);
37373         }
37374     }
37375     
37376     if(this.tabPosition == "bottom"){
37377         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37378         this.el.addClass("roo-tabs-bottom");
37379     }
37380     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37381     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37382     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37383     if(Roo.isIE){
37384         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37385     }
37386     if(this.tabPosition != "bottom"){
37387         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37388          * @type Roo.Element
37389          */
37390         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37391         this.el.addClass("roo-tabs-top");
37392     }
37393     this.items = [];
37394
37395     this.bodyEl.setStyle("position", "relative");
37396
37397     this.active = null;
37398     this.activateDelegate = this.activate.createDelegate(this);
37399
37400     this.addEvents({
37401         /**
37402          * @event tabchange
37403          * Fires when the active tab changes
37404          * @param {Roo.TabPanel} this
37405          * @param {Roo.TabPanelItem} activePanel The new active tab
37406          */
37407         "tabchange": true,
37408         /**
37409          * @event beforetabchange
37410          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37411          * @param {Roo.TabPanel} this
37412          * @param {Object} e Set cancel to true on this object to cancel the tab change
37413          * @param {Roo.TabPanelItem} tab The tab being changed to
37414          */
37415         "beforetabchange" : true
37416     });
37417
37418     Roo.EventManager.onWindowResize(this.onResize, this);
37419     this.cpad = this.el.getPadding("lr");
37420     this.hiddenCount = 0;
37421
37422
37423     // toolbar on the tabbar support...
37424     if (this.toolbar) {
37425         alert("no toolbar support yet");
37426         this.toolbar  = false;
37427         /*
37428         var tcfg = this.toolbar;
37429         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37430         this.toolbar = new Roo.Toolbar(tcfg);
37431         if (Roo.isSafari) {
37432             var tbl = tcfg.container.child('table', true);
37433             tbl.setAttribute('width', '100%');
37434         }
37435         */
37436         
37437     }
37438    
37439
37440
37441     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37442 };
37443
37444 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37445     /*
37446      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37447      */
37448     tabPosition : "top",
37449     /*
37450      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37451      */
37452     currentTabWidth : 0,
37453     /*
37454      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37455      */
37456     minTabWidth : 40,
37457     /*
37458      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37459      */
37460     maxTabWidth : 250,
37461     /*
37462      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37463      */
37464     preferredTabWidth : 175,
37465     /*
37466      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37467      */
37468     resizeTabs : false,
37469     /*
37470      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37471      */
37472     monitorResize : true,
37473     /*
37474      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37475      */
37476     toolbar : false,
37477
37478     /**
37479      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37480      * @param {String} id The id of the div to use <b>or create</b>
37481      * @param {String} text The text for the tab
37482      * @param {String} content (optional) Content to put in the TabPanelItem body
37483      * @param {Boolean} closable (optional) True to create a close icon on the tab
37484      * @return {Roo.TabPanelItem} The created TabPanelItem
37485      */
37486     addTab : function(id, text, content, closable, tpl)
37487     {
37488         var item = new Roo.bootstrap.panel.TabItem({
37489             panel: this,
37490             id : id,
37491             text : text,
37492             closable : closable,
37493             tpl : tpl
37494         });
37495         this.addTabItem(item);
37496         if(content){
37497             item.setContent(content);
37498         }
37499         return item;
37500     },
37501
37502     /**
37503      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37504      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37505      * @return {Roo.TabPanelItem}
37506      */
37507     getTab : function(id){
37508         return this.items[id];
37509     },
37510
37511     /**
37512      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37513      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37514      */
37515     hideTab : function(id){
37516         var t = this.items[id];
37517         if(!t.isHidden()){
37518            t.setHidden(true);
37519            this.hiddenCount++;
37520            this.autoSizeTabs();
37521         }
37522     },
37523
37524     /**
37525      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37526      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37527      */
37528     unhideTab : function(id){
37529         var t = this.items[id];
37530         if(t.isHidden()){
37531            t.setHidden(false);
37532            this.hiddenCount--;
37533            this.autoSizeTabs();
37534         }
37535     },
37536
37537     /**
37538      * Adds an existing {@link Roo.TabPanelItem}.
37539      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37540      */
37541     addTabItem : function(item){
37542         this.items[item.id] = item;
37543         this.items.push(item);
37544       //  if(this.resizeTabs){
37545     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37546   //         this.autoSizeTabs();
37547 //        }else{
37548 //            item.autoSize();
37549        // }
37550     },
37551
37552     /**
37553      * Removes a {@link Roo.TabPanelItem}.
37554      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37555      */
37556     removeTab : function(id){
37557         var items = this.items;
37558         var tab = items[id];
37559         if(!tab) { return; }
37560         var index = items.indexOf(tab);
37561         if(this.active == tab && items.length > 1){
37562             var newTab = this.getNextAvailable(index);
37563             if(newTab) {
37564                 newTab.activate();
37565             }
37566         }
37567         this.stripEl.dom.removeChild(tab.pnode.dom);
37568         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37569             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37570         }
37571         items.splice(index, 1);
37572         delete this.items[tab.id];
37573         tab.fireEvent("close", tab);
37574         tab.purgeListeners();
37575         this.autoSizeTabs();
37576     },
37577
37578     getNextAvailable : function(start){
37579         var items = this.items;
37580         var index = start;
37581         // look for a next tab that will slide over to
37582         // replace the one being removed
37583         while(index < items.length){
37584             var item = items[++index];
37585             if(item && !item.isHidden()){
37586                 return item;
37587             }
37588         }
37589         // if one isn't found select the previous tab (on the left)
37590         index = start;
37591         while(index >= 0){
37592             var item = items[--index];
37593             if(item && !item.isHidden()){
37594                 return item;
37595             }
37596         }
37597         return null;
37598     },
37599
37600     /**
37601      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37602      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37603      */
37604     disableTab : function(id){
37605         var tab = this.items[id];
37606         if(tab && this.active != tab){
37607             tab.disable();
37608         }
37609     },
37610
37611     /**
37612      * Enables a {@link Roo.TabPanelItem} that is disabled.
37613      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37614      */
37615     enableTab : function(id){
37616         var tab = this.items[id];
37617         tab.enable();
37618     },
37619
37620     /**
37621      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37622      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37623      * @return {Roo.TabPanelItem} The TabPanelItem.
37624      */
37625     activate : function(id){
37626         var tab = this.items[id];
37627         if(!tab){
37628             return null;
37629         }
37630         if(tab == this.active || tab.disabled){
37631             return tab;
37632         }
37633         var e = {};
37634         this.fireEvent("beforetabchange", this, e, tab);
37635         if(e.cancel !== true && !tab.disabled){
37636             if(this.active){
37637                 this.active.hide();
37638             }
37639             this.active = this.items[id];
37640             this.active.show();
37641             this.fireEvent("tabchange", this, this.active);
37642         }
37643         return tab;
37644     },
37645
37646     /**
37647      * Gets the active {@link Roo.TabPanelItem}.
37648      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37649      */
37650     getActiveTab : function(){
37651         return this.active;
37652     },
37653
37654     /**
37655      * Updates the tab body element to fit the height of the container element
37656      * for overflow scrolling
37657      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37658      */
37659     syncHeight : function(targetHeight){
37660         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37661         var bm = this.bodyEl.getMargins();
37662         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37663         this.bodyEl.setHeight(newHeight);
37664         return newHeight;
37665     },
37666
37667     onResize : function(){
37668         if(this.monitorResize){
37669             this.autoSizeTabs();
37670         }
37671     },
37672
37673     /**
37674      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37675      */
37676     beginUpdate : function(){
37677         this.updating = true;
37678     },
37679
37680     /**
37681      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37682      */
37683     endUpdate : function(){
37684         this.updating = false;
37685         this.autoSizeTabs();
37686     },
37687
37688     /**
37689      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37690      */
37691     autoSizeTabs : function(){
37692         var count = this.items.length;
37693         var vcount = count - this.hiddenCount;
37694         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37695             return;
37696         }
37697         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37698         var availWidth = Math.floor(w / vcount);
37699         var b = this.stripBody;
37700         if(b.getWidth() > w){
37701             var tabs = this.items;
37702             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37703             if(availWidth < this.minTabWidth){
37704                 /*if(!this.sleft){    // incomplete scrolling code
37705                     this.createScrollButtons();
37706                 }
37707                 this.showScroll();
37708                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37709             }
37710         }else{
37711             if(this.currentTabWidth < this.preferredTabWidth){
37712                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37713             }
37714         }
37715     },
37716
37717     /**
37718      * Returns the number of tabs in this TabPanel.
37719      * @return {Number}
37720      */
37721      getCount : function(){
37722          return this.items.length;
37723      },
37724
37725     /**
37726      * Resizes all the tabs to the passed width
37727      * @param {Number} The new width
37728      */
37729     setTabWidth : function(width){
37730         this.currentTabWidth = width;
37731         for(var i = 0, len = this.items.length; i < len; i++) {
37732                 if(!this.items[i].isHidden()) {
37733                 this.items[i].setWidth(width);
37734             }
37735         }
37736     },
37737
37738     /**
37739      * Destroys this TabPanel
37740      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37741      */
37742     destroy : function(removeEl){
37743         Roo.EventManager.removeResizeListener(this.onResize, this);
37744         for(var i = 0, len = this.items.length; i < len; i++){
37745             this.items[i].purgeListeners();
37746         }
37747         if(removeEl === true){
37748             this.el.update("");
37749             this.el.remove();
37750         }
37751     },
37752     
37753     createStrip : function(container)
37754     {
37755         var strip = document.createElement("nav");
37756         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37757         container.appendChild(strip);
37758         return strip;
37759     },
37760     
37761     createStripList : function(strip)
37762     {
37763         // div wrapper for retard IE
37764         // returns the "tr" element.
37765         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37766         //'<div class="x-tabs-strip-wrap">'+
37767           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37768           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37769         return strip.firstChild; //.firstChild.firstChild.firstChild;
37770     },
37771     createBody : function(container)
37772     {
37773         var body = document.createElement("div");
37774         Roo.id(body, "tab-body");
37775         //Roo.fly(body).addClass("x-tabs-body");
37776         Roo.fly(body).addClass("tab-content");
37777         container.appendChild(body);
37778         return body;
37779     },
37780     createItemBody :function(bodyEl, id){
37781         var body = Roo.getDom(id);
37782         if(!body){
37783             body = document.createElement("div");
37784             body.id = id;
37785         }
37786         //Roo.fly(body).addClass("x-tabs-item-body");
37787         Roo.fly(body).addClass("tab-pane");
37788          bodyEl.insertBefore(body, bodyEl.firstChild);
37789         return body;
37790     },
37791     /** @private */
37792     createStripElements :  function(stripEl, text, closable, tpl)
37793     {
37794         var td = document.createElement("li"); // was td..
37795         
37796         
37797         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37798         
37799         
37800         stripEl.appendChild(td);
37801         /*if(closable){
37802             td.className = "x-tabs-closable";
37803             if(!this.closeTpl){
37804                 this.closeTpl = new Roo.Template(
37805                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37806                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37807                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37808                 );
37809             }
37810             var el = this.closeTpl.overwrite(td, {"text": text});
37811             var close = el.getElementsByTagName("div")[0];
37812             var inner = el.getElementsByTagName("em")[0];
37813             return {"el": el, "close": close, "inner": inner};
37814         } else {
37815         */
37816         // not sure what this is..
37817 //            if(!this.tabTpl){
37818                 //this.tabTpl = new Roo.Template(
37819                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37820                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37821                 //);
37822 //                this.tabTpl = new Roo.Template(
37823 //                   '<a href="#">' +
37824 //                   '<span unselectable="on"' +
37825 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37826 //                            ' >{text}</span></a>'
37827 //                );
37828 //                
37829 //            }
37830
37831
37832             var template = tpl || this.tabTpl || false;
37833             
37834             if(!template){
37835                 
37836                 template = new Roo.Template(
37837                    '<a href="#">' +
37838                    '<span unselectable="on"' +
37839                             (this.disableTooltips ? '' : ' title="{text}"') +
37840                             ' >{text}</span></a>'
37841                 );
37842             }
37843             
37844             switch (typeof(template)) {
37845                 case 'object' :
37846                     break;
37847                 case 'string' :
37848                     template = new Roo.Template(template);
37849                     break;
37850                 default :
37851                     break;
37852             }
37853             
37854             var el = template.overwrite(td, {"text": text});
37855             
37856             var inner = el.getElementsByTagName("span")[0];
37857             
37858             return {"el": el, "inner": inner};
37859             
37860     }
37861         
37862     
37863 });
37864
37865 /**
37866  * @class Roo.TabPanelItem
37867  * @extends Roo.util.Observable
37868  * Represents an individual item (tab plus body) in a TabPanel.
37869  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37870  * @param {String} id The id of this TabPanelItem
37871  * @param {String} text The text for the tab of this TabPanelItem
37872  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37873  */
37874 Roo.bootstrap.panel.TabItem = function(config){
37875     /**
37876      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37877      * @type Roo.TabPanel
37878      */
37879     this.tabPanel = config.panel;
37880     /**
37881      * The id for this TabPanelItem
37882      * @type String
37883      */
37884     this.id = config.id;
37885     /** @private */
37886     this.disabled = false;
37887     /** @private */
37888     this.text = config.text;
37889     /** @private */
37890     this.loaded = false;
37891     this.closable = config.closable;
37892
37893     /**
37894      * The body element for this TabPanelItem.
37895      * @type Roo.Element
37896      */
37897     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37898     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37899     this.bodyEl.setStyle("display", "block");
37900     this.bodyEl.setStyle("zoom", "1");
37901     //this.hideAction();
37902
37903     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37904     /** @private */
37905     this.el = Roo.get(els.el);
37906     this.inner = Roo.get(els.inner, true);
37907     this.textEl = Roo.get(this.el.dom.firstChild, true);
37908     this.pnode = Roo.get(els.el.parentNode, true);
37909 //    this.el.on("mousedown", this.onTabMouseDown, this);
37910     this.el.on("click", this.onTabClick, this);
37911     /** @private */
37912     if(config.closable){
37913         var c = Roo.get(els.close, true);
37914         c.dom.title = this.closeText;
37915         c.addClassOnOver("close-over");
37916         c.on("click", this.closeClick, this);
37917      }
37918
37919     this.addEvents({
37920          /**
37921          * @event activate
37922          * Fires when this tab becomes the active tab.
37923          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37924          * @param {Roo.TabPanelItem} this
37925          */
37926         "activate": true,
37927         /**
37928          * @event beforeclose
37929          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37930          * @param {Roo.TabPanelItem} this
37931          * @param {Object} e Set cancel to true on this object to cancel the close.
37932          */
37933         "beforeclose": true,
37934         /**
37935          * @event close
37936          * Fires when this tab is closed.
37937          * @param {Roo.TabPanelItem} this
37938          */
37939          "close": true,
37940         /**
37941          * @event deactivate
37942          * Fires when this tab is no longer the active tab.
37943          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37944          * @param {Roo.TabPanelItem} this
37945          */
37946          "deactivate" : true
37947     });
37948     this.hidden = false;
37949
37950     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37951 };
37952
37953 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37954            {
37955     purgeListeners : function(){
37956        Roo.util.Observable.prototype.purgeListeners.call(this);
37957        this.el.removeAllListeners();
37958     },
37959     /**
37960      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37961      */
37962     show : function(){
37963         this.pnode.addClass("active");
37964         this.showAction();
37965         if(Roo.isOpera){
37966             this.tabPanel.stripWrap.repaint();
37967         }
37968         this.fireEvent("activate", this.tabPanel, this);
37969     },
37970
37971     /**
37972      * Returns true if this tab is the active tab.
37973      * @return {Boolean}
37974      */
37975     isActive : function(){
37976         return this.tabPanel.getActiveTab() == this;
37977     },
37978
37979     /**
37980      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37981      */
37982     hide : function(){
37983         this.pnode.removeClass("active");
37984         this.hideAction();
37985         this.fireEvent("deactivate", this.tabPanel, this);
37986     },
37987
37988     hideAction : function(){
37989         this.bodyEl.hide();
37990         this.bodyEl.setStyle("position", "absolute");
37991         this.bodyEl.setLeft("-20000px");
37992         this.bodyEl.setTop("-20000px");
37993     },
37994
37995     showAction : function(){
37996         this.bodyEl.setStyle("position", "relative");
37997         this.bodyEl.setTop("");
37998         this.bodyEl.setLeft("");
37999         this.bodyEl.show();
38000     },
38001
38002     /**
38003      * Set the tooltip for the tab.
38004      * @param {String} tooltip The tab's tooltip
38005      */
38006     setTooltip : function(text){
38007         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38008             this.textEl.dom.qtip = text;
38009             this.textEl.dom.removeAttribute('title');
38010         }else{
38011             this.textEl.dom.title = text;
38012         }
38013     },
38014
38015     onTabClick : function(e){
38016         e.preventDefault();
38017         this.tabPanel.activate(this.id);
38018     },
38019
38020     onTabMouseDown : function(e){
38021         e.preventDefault();
38022         this.tabPanel.activate(this.id);
38023     },
38024 /*
38025     getWidth : function(){
38026         return this.inner.getWidth();
38027     },
38028
38029     setWidth : function(width){
38030         var iwidth = width - this.pnode.getPadding("lr");
38031         this.inner.setWidth(iwidth);
38032         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38033         this.pnode.setWidth(width);
38034     },
38035 */
38036     /**
38037      * Show or hide the tab
38038      * @param {Boolean} hidden True to hide or false to show.
38039      */
38040     setHidden : function(hidden){
38041         this.hidden = hidden;
38042         this.pnode.setStyle("display", hidden ? "none" : "");
38043     },
38044
38045     /**
38046      * Returns true if this tab is "hidden"
38047      * @return {Boolean}
38048      */
38049     isHidden : function(){
38050         return this.hidden;
38051     },
38052
38053     /**
38054      * Returns the text for this tab
38055      * @return {String}
38056      */
38057     getText : function(){
38058         return this.text;
38059     },
38060     /*
38061     autoSize : function(){
38062         //this.el.beginMeasure();
38063         this.textEl.setWidth(1);
38064         /*
38065          *  #2804 [new] Tabs in Roojs
38066          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38067          */
38068         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38069         //this.el.endMeasure();
38070     //},
38071
38072     /**
38073      * Sets the text for the tab (Note: this also sets the tooltip text)
38074      * @param {String} text The tab's text and tooltip
38075      */
38076     setText : function(text){
38077         this.text = text;
38078         this.textEl.update(text);
38079         this.setTooltip(text);
38080         //if(!this.tabPanel.resizeTabs){
38081         //    this.autoSize();
38082         //}
38083     },
38084     /**
38085      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38086      */
38087     activate : function(){
38088         this.tabPanel.activate(this.id);
38089     },
38090
38091     /**
38092      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38093      */
38094     disable : function(){
38095         if(this.tabPanel.active != this){
38096             this.disabled = true;
38097             this.pnode.addClass("disabled");
38098         }
38099     },
38100
38101     /**
38102      * Enables this TabPanelItem if it was previously disabled.
38103      */
38104     enable : function(){
38105         this.disabled = false;
38106         this.pnode.removeClass("disabled");
38107     },
38108
38109     /**
38110      * Sets the content for this TabPanelItem.
38111      * @param {String} content The content
38112      * @param {Boolean} loadScripts true to look for and load scripts
38113      */
38114     setContent : function(content, loadScripts){
38115         this.bodyEl.update(content, loadScripts);
38116     },
38117
38118     /**
38119      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38120      * @return {Roo.UpdateManager} The UpdateManager
38121      */
38122     getUpdateManager : function(){
38123         return this.bodyEl.getUpdateManager();
38124     },
38125
38126     /**
38127      * Set a URL to be used to load the content for this TabPanelItem.
38128      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38129      * @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)
38130      * @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)
38131      * @return {Roo.UpdateManager} The UpdateManager
38132      */
38133     setUrl : function(url, params, loadOnce){
38134         if(this.refreshDelegate){
38135             this.un('activate', this.refreshDelegate);
38136         }
38137         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38138         this.on("activate", this.refreshDelegate);
38139         return this.bodyEl.getUpdateManager();
38140     },
38141
38142     /** @private */
38143     _handleRefresh : function(url, params, loadOnce){
38144         if(!loadOnce || !this.loaded){
38145             var updater = this.bodyEl.getUpdateManager();
38146             updater.update(url, params, this._setLoaded.createDelegate(this));
38147         }
38148     },
38149
38150     /**
38151      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38152      *   Will fail silently if the setUrl method has not been called.
38153      *   This does not activate the panel, just updates its content.
38154      */
38155     refresh : function(){
38156         if(this.refreshDelegate){
38157            this.loaded = false;
38158            this.refreshDelegate();
38159         }
38160     },
38161
38162     /** @private */
38163     _setLoaded : function(){
38164         this.loaded = true;
38165     },
38166
38167     /** @private */
38168     closeClick : function(e){
38169         var o = {};
38170         e.stopEvent();
38171         this.fireEvent("beforeclose", this, o);
38172         if(o.cancel !== true){
38173             this.tabPanel.removeTab(this.id);
38174         }
38175     },
38176     /**
38177      * The text displayed in the tooltip for the close icon.
38178      * @type String
38179      */
38180     closeText : "Close this tab"
38181 });
38182 /**
38183 *    This script refer to:
38184 *    Title: International Telephone Input
38185 *    Author: Jack O'Connor
38186 *    Code version:  v12.1.12
38187 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38188 **/
38189
38190 Roo.bootstrap.PhoneInputData = function() {
38191     var d = [
38192       [
38193         "Afghanistan (‫افغانستان‬‎)",
38194         "af",
38195         "93"
38196       ],
38197       [
38198         "Albania (Shqipëri)",
38199         "al",
38200         "355"
38201       ],
38202       [
38203         "Algeria (‫الجزائر‬‎)",
38204         "dz",
38205         "213"
38206       ],
38207       [
38208         "American Samoa",
38209         "as",
38210         "1684"
38211       ],
38212       [
38213         "Andorra",
38214         "ad",
38215         "376"
38216       ],
38217       [
38218         "Angola",
38219         "ao",
38220         "244"
38221       ],
38222       [
38223         "Anguilla",
38224         "ai",
38225         "1264"
38226       ],
38227       [
38228         "Antigua and Barbuda",
38229         "ag",
38230         "1268"
38231       ],
38232       [
38233         "Argentina",
38234         "ar",
38235         "54"
38236       ],
38237       [
38238         "Armenia (Հայաստան)",
38239         "am",
38240         "374"
38241       ],
38242       [
38243         "Aruba",
38244         "aw",
38245         "297"
38246       ],
38247       [
38248         "Australia",
38249         "au",
38250         "61",
38251         0
38252       ],
38253       [
38254         "Austria (Österreich)",
38255         "at",
38256         "43"
38257       ],
38258       [
38259         "Azerbaijan (Azərbaycan)",
38260         "az",
38261         "994"
38262       ],
38263       [
38264         "Bahamas",
38265         "bs",
38266         "1242"
38267       ],
38268       [
38269         "Bahrain (‫البحرين‬‎)",
38270         "bh",
38271         "973"
38272       ],
38273       [
38274         "Bangladesh (বাংলাদেশ)",
38275         "bd",
38276         "880"
38277       ],
38278       [
38279         "Barbados",
38280         "bb",
38281         "1246"
38282       ],
38283       [
38284         "Belarus (Беларусь)",
38285         "by",
38286         "375"
38287       ],
38288       [
38289         "Belgium (België)",
38290         "be",
38291         "32"
38292       ],
38293       [
38294         "Belize",
38295         "bz",
38296         "501"
38297       ],
38298       [
38299         "Benin (Bénin)",
38300         "bj",
38301         "229"
38302       ],
38303       [
38304         "Bermuda",
38305         "bm",
38306         "1441"
38307       ],
38308       [
38309         "Bhutan (འབྲུག)",
38310         "bt",
38311         "975"
38312       ],
38313       [
38314         "Bolivia",
38315         "bo",
38316         "591"
38317       ],
38318       [
38319         "Bosnia and Herzegovina (Босна и Херцеговина)",
38320         "ba",
38321         "387"
38322       ],
38323       [
38324         "Botswana",
38325         "bw",
38326         "267"
38327       ],
38328       [
38329         "Brazil (Brasil)",
38330         "br",
38331         "55"
38332       ],
38333       [
38334         "British Indian Ocean Territory",
38335         "io",
38336         "246"
38337       ],
38338       [
38339         "British Virgin Islands",
38340         "vg",
38341         "1284"
38342       ],
38343       [
38344         "Brunei",
38345         "bn",
38346         "673"
38347       ],
38348       [
38349         "Bulgaria (България)",
38350         "bg",
38351         "359"
38352       ],
38353       [
38354         "Burkina Faso",
38355         "bf",
38356         "226"
38357       ],
38358       [
38359         "Burundi (Uburundi)",
38360         "bi",
38361         "257"
38362       ],
38363       [
38364         "Cambodia (កម្ពុជា)",
38365         "kh",
38366         "855"
38367       ],
38368       [
38369         "Cameroon (Cameroun)",
38370         "cm",
38371         "237"
38372       ],
38373       [
38374         "Canada",
38375         "ca",
38376         "1",
38377         1,
38378         ["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"]
38379       ],
38380       [
38381         "Cape Verde (Kabu Verdi)",
38382         "cv",
38383         "238"
38384       ],
38385       [
38386         "Caribbean Netherlands",
38387         "bq",
38388         "599",
38389         1
38390       ],
38391       [
38392         "Cayman Islands",
38393         "ky",
38394         "1345"
38395       ],
38396       [
38397         "Central African Republic (République centrafricaine)",
38398         "cf",
38399         "236"
38400       ],
38401       [
38402         "Chad (Tchad)",
38403         "td",
38404         "235"
38405       ],
38406       [
38407         "Chile",
38408         "cl",
38409         "56"
38410       ],
38411       [
38412         "China (中国)",
38413         "cn",
38414         "86"
38415       ],
38416       [
38417         "Christmas Island",
38418         "cx",
38419         "61",
38420         2
38421       ],
38422       [
38423         "Cocos (Keeling) Islands",
38424         "cc",
38425         "61",
38426         1
38427       ],
38428       [
38429         "Colombia",
38430         "co",
38431         "57"
38432       ],
38433       [
38434         "Comoros (‫جزر القمر‬‎)",
38435         "km",
38436         "269"
38437       ],
38438       [
38439         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38440         "cd",
38441         "243"
38442       ],
38443       [
38444         "Congo (Republic) (Congo-Brazzaville)",
38445         "cg",
38446         "242"
38447       ],
38448       [
38449         "Cook Islands",
38450         "ck",
38451         "682"
38452       ],
38453       [
38454         "Costa Rica",
38455         "cr",
38456         "506"
38457       ],
38458       [
38459         "Côte d’Ivoire",
38460         "ci",
38461         "225"
38462       ],
38463       [
38464         "Croatia (Hrvatska)",
38465         "hr",
38466         "385"
38467       ],
38468       [
38469         "Cuba",
38470         "cu",
38471         "53"
38472       ],
38473       [
38474         "Curaçao",
38475         "cw",
38476         "599",
38477         0
38478       ],
38479       [
38480         "Cyprus (Κύπρος)",
38481         "cy",
38482         "357"
38483       ],
38484       [
38485         "Czech Republic (Česká republika)",
38486         "cz",
38487         "420"
38488       ],
38489       [
38490         "Denmark (Danmark)",
38491         "dk",
38492         "45"
38493       ],
38494       [
38495         "Djibouti",
38496         "dj",
38497         "253"
38498       ],
38499       [
38500         "Dominica",
38501         "dm",
38502         "1767"
38503       ],
38504       [
38505         "Dominican Republic (República Dominicana)",
38506         "do",
38507         "1",
38508         2,
38509         ["809", "829", "849"]
38510       ],
38511       [
38512         "Ecuador",
38513         "ec",
38514         "593"
38515       ],
38516       [
38517         "Egypt (‫مصر‬‎)",
38518         "eg",
38519         "20"
38520       ],
38521       [
38522         "El Salvador",
38523         "sv",
38524         "503"
38525       ],
38526       [
38527         "Equatorial Guinea (Guinea Ecuatorial)",
38528         "gq",
38529         "240"
38530       ],
38531       [
38532         "Eritrea",
38533         "er",
38534         "291"
38535       ],
38536       [
38537         "Estonia (Eesti)",
38538         "ee",
38539         "372"
38540       ],
38541       [
38542         "Ethiopia",
38543         "et",
38544         "251"
38545       ],
38546       [
38547         "Falkland Islands (Islas Malvinas)",
38548         "fk",
38549         "500"
38550       ],
38551       [
38552         "Faroe Islands (Føroyar)",
38553         "fo",
38554         "298"
38555       ],
38556       [
38557         "Fiji",
38558         "fj",
38559         "679"
38560       ],
38561       [
38562         "Finland (Suomi)",
38563         "fi",
38564         "358",
38565         0
38566       ],
38567       [
38568         "France",
38569         "fr",
38570         "33"
38571       ],
38572       [
38573         "French Guiana (Guyane française)",
38574         "gf",
38575         "594"
38576       ],
38577       [
38578         "French Polynesia (Polynésie française)",
38579         "pf",
38580         "689"
38581       ],
38582       [
38583         "Gabon",
38584         "ga",
38585         "241"
38586       ],
38587       [
38588         "Gambia",
38589         "gm",
38590         "220"
38591       ],
38592       [
38593         "Georgia (საქართველო)",
38594         "ge",
38595         "995"
38596       ],
38597       [
38598         "Germany (Deutschland)",
38599         "de",
38600         "49"
38601       ],
38602       [
38603         "Ghana (Gaana)",
38604         "gh",
38605         "233"
38606       ],
38607       [
38608         "Gibraltar",
38609         "gi",
38610         "350"
38611       ],
38612       [
38613         "Greece (Ελλάδα)",
38614         "gr",
38615         "30"
38616       ],
38617       [
38618         "Greenland (Kalaallit Nunaat)",
38619         "gl",
38620         "299"
38621       ],
38622       [
38623         "Grenada",
38624         "gd",
38625         "1473"
38626       ],
38627       [
38628         "Guadeloupe",
38629         "gp",
38630         "590",
38631         0
38632       ],
38633       [
38634         "Guam",
38635         "gu",
38636         "1671"
38637       ],
38638       [
38639         "Guatemala",
38640         "gt",
38641         "502"
38642       ],
38643       [
38644         "Guernsey",
38645         "gg",
38646         "44",
38647         1
38648       ],
38649       [
38650         "Guinea (Guinée)",
38651         "gn",
38652         "224"
38653       ],
38654       [
38655         "Guinea-Bissau (Guiné Bissau)",
38656         "gw",
38657         "245"
38658       ],
38659       [
38660         "Guyana",
38661         "gy",
38662         "592"
38663       ],
38664       [
38665         "Haiti",
38666         "ht",
38667         "509"
38668       ],
38669       [
38670         "Honduras",
38671         "hn",
38672         "504"
38673       ],
38674       [
38675         "Hong Kong (香港)",
38676         "hk",
38677         "852"
38678       ],
38679       [
38680         "Hungary (Magyarország)",
38681         "hu",
38682         "36"
38683       ],
38684       [
38685         "Iceland (Ísland)",
38686         "is",
38687         "354"
38688       ],
38689       [
38690         "India (भारत)",
38691         "in",
38692         "91"
38693       ],
38694       [
38695         "Indonesia",
38696         "id",
38697         "62"
38698       ],
38699       [
38700         "Iran (‫ایران‬‎)",
38701         "ir",
38702         "98"
38703       ],
38704       [
38705         "Iraq (‫العراق‬‎)",
38706         "iq",
38707         "964"
38708       ],
38709       [
38710         "Ireland",
38711         "ie",
38712         "353"
38713       ],
38714       [
38715         "Isle of Man",
38716         "im",
38717         "44",
38718         2
38719       ],
38720       [
38721         "Israel (‫ישראל‬‎)",
38722         "il",
38723         "972"
38724       ],
38725       [
38726         "Italy (Italia)",
38727         "it",
38728         "39",
38729         0
38730       ],
38731       [
38732         "Jamaica",
38733         "jm",
38734         "1876"
38735       ],
38736       [
38737         "Japan (日本)",
38738         "jp",
38739         "81"
38740       ],
38741       [
38742         "Jersey",
38743         "je",
38744         "44",
38745         3
38746       ],
38747       [
38748         "Jordan (‫الأردن‬‎)",
38749         "jo",
38750         "962"
38751       ],
38752       [
38753         "Kazakhstan (Казахстан)",
38754         "kz",
38755         "7",
38756         1
38757       ],
38758       [
38759         "Kenya",
38760         "ke",
38761         "254"
38762       ],
38763       [
38764         "Kiribati",
38765         "ki",
38766         "686"
38767       ],
38768       [
38769         "Kosovo",
38770         "xk",
38771         "383"
38772       ],
38773       [
38774         "Kuwait (‫الكويت‬‎)",
38775         "kw",
38776         "965"
38777       ],
38778       [
38779         "Kyrgyzstan (Кыргызстан)",
38780         "kg",
38781         "996"
38782       ],
38783       [
38784         "Laos (ລາວ)",
38785         "la",
38786         "856"
38787       ],
38788       [
38789         "Latvia (Latvija)",
38790         "lv",
38791         "371"
38792       ],
38793       [
38794         "Lebanon (‫لبنان‬‎)",
38795         "lb",
38796         "961"
38797       ],
38798       [
38799         "Lesotho",
38800         "ls",
38801         "266"
38802       ],
38803       [
38804         "Liberia",
38805         "lr",
38806         "231"
38807       ],
38808       [
38809         "Libya (‫ليبيا‬‎)",
38810         "ly",
38811         "218"
38812       ],
38813       [
38814         "Liechtenstein",
38815         "li",
38816         "423"
38817       ],
38818       [
38819         "Lithuania (Lietuva)",
38820         "lt",
38821         "370"
38822       ],
38823       [
38824         "Luxembourg",
38825         "lu",
38826         "352"
38827       ],
38828       [
38829         "Macau (澳門)",
38830         "mo",
38831         "853"
38832       ],
38833       [
38834         "Macedonia (FYROM) (Македонија)",
38835         "mk",
38836         "389"
38837       ],
38838       [
38839         "Madagascar (Madagasikara)",
38840         "mg",
38841         "261"
38842       ],
38843       [
38844         "Malawi",
38845         "mw",
38846         "265"
38847       ],
38848       [
38849         "Malaysia",
38850         "my",
38851         "60"
38852       ],
38853       [
38854         "Maldives",
38855         "mv",
38856         "960"
38857       ],
38858       [
38859         "Mali",
38860         "ml",
38861         "223"
38862       ],
38863       [
38864         "Malta",
38865         "mt",
38866         "356"
38867       ],
38868       [
38869         "Marshall Islands",
38870         "mh",
38871         "692"
38872       ],
38873       [
38874         "Martinique",
38875         "mq",
38876         "596"
38877       ],
38878       [
38879         "Mauritania (‫موريتانيا‬‎)",
38880         "mr",
38881         "222"
38882       ],
38883       [
38884         "Mauritius (Moris)",
38885         "mu",
38886         "230"
38887       ],
38888       [
38889         "Mayotte",
38890         "yt",
38891         "262",
38892         1
38893       ],
38894       [
38895         "Mexico (México)",
38896         "mx",
38897         "52"
38898       ],
38899       [
38900         "Micronesia",
38901         "fm",
38902         "691"
38903       ],
38904       [
38905         "Moldova (Republica Moldova)",
38906         "md",
38907         "373"
38908       ],
38909       [
38910         "Monaco",
38911         "mc",
38912         "377"
38913       ],
38914       [
38915         "Mongolia (Монгол)",
38916         "mn",
38917         "976"
38918       ],
38919       [
38920         "Montenegro (Crna Gora)",
38921         "me",
38922         "382"
38923       ],
38924       [
38925         "Montserrat",
38926         "ms",
38927         "1664"
38928       ],
38929       [
38930         "Morocco (‫المغرب‬‎)",
38931         "ma",
38932         "212",
38933         0
38934       ],
38935       [
38936         "Mozambique (Moçambique)",
38937         "mz",
38938         "258"
38939       ],
38940       [
38941         "Myanmar (Burma) (မြန်မာ)",
38942         "mm",
38943         "95"
38944       ],
38945       [
38946         "Namibia (Namibië)",
38947         "na",
38948         "264"
38949       ],
38950       [
38951         "Nauru",
38952         "nr",
38953         "674"
38954       ],
38955       [
38956         "Nepal (नेपाल)",
38957         "np",
38958         "977"
38959       ],
38960       [
38961         "Netherlands (Nederland)",
38962         "nl",
38963         "31"
38964       ],
38965       [
38966         "New Caledonia (Nouvelle-Calédonie)",
38967         "nc",
38968         "687"
38969       ],
38970       [
38971         "New Zealand",
38972         "nz",
38973         "64"
38974       ],
38975       [
38976         "Nicaragua",
38977         "ni",
38978         "505"
38979       ],
38980       [
38981         "Niger (Nijar)",
38982         "ne",
38983         "227"
38984       ],
38985       [
38986         "Nigeria",
38987         "ng",
38988         "234"
38989       ],
38990       [
38991         "Niue",
38992         "nu",
38993         "683"
38994       ],
38995       [
38996         "Norfolk Island",
38997         "nf",
38998         "672"
38999       ],
39000       [
39001         "North Korea (조선 민주주의 인민 공화국)",
39002         "kp",
39003         "850"
39004       ],
39005       [
39006         "Northern Mariana Islands",
39007         "mp",
39008         "1670"
39009       ],
39010       [
39011         "Norway (Norge)",
39012         "no",
39013         "47",
39014         0
39015       ],
39016       [
39017         "Oman (‫عُمان‬‎)",
39018         "om",
39019         "968"
39020       ],
39021       [
39022         "Pakistan (‫پاکستان‬‎)",
39023         "pk",
39024         "92"
39025       ],
39026       [
39027         "Palau",
39028         "pw",
39029         "680"
39030       ],
39031       [
39032         "Palestine (‫فلسطين‬‎)",
39033         "ps",
39034         "970"
39035       ],
39036       [
39037         "Panama (Panamá)",
39038         "pa",
39039         "507"
39040       ],
39041       [
39042         "Papua New Guinea",
39043         "pg",
39044         "675"
39045       ],
39046       [
39047         "Paraguay",
39048         "py",
39049         "595"
39050       ],
39051       [
39052         "Peru (Perú)",
39053         "pe",
39054         "51"
39055       ],
39056       [
39057         "Philippines",
39058         "ph",
39059         "63"
39060       ],
39061       [
39062         "Poland (Polska)",
39063         "pl",
39064         "48"
39065       ],
39066       [
39067         "Portugal",
39068         "pt",
39069         "351"
39070       ],
39071       [
39072         "Puerto Rico",
39073         "pr",
39074         "1",
39075         3,
39076         ["787", "939"]
39077       ],
39078       [
39079         "Qatar (‫قطر‬‎)",
39080         "qa",
39081         "974"
39082       ],
39083       [
39084         "Réunion (La Réunion)",
39085         "re",
39086         "262",
39087         0
39088       ],
39089       [
39090         "Romania (România)",
39091         "ro",
39092         "40"
39093       ],
39094       [
39095         "Russia (Россия)",
39096         "ru",
39097         "7",
39098         0
39099       ],
39100       [
39101         "Rwanda",
39102         "rw",
39103         "250"
39104       ],
39105       [
39106         "Saint Barthélemy",
39107         "bl",
39108         "590",
39109         1
39110       ],
39111       [
39112         "Saint Helena",
39113         "sh",
39114         "290"
39115       ],
39116       [
39117         "Saint Kitts and Nevis",
39118         "kn",
39119         "1869"
39120       ],
39121       [
39122         "Saint Lucia",
39123         "lc",
39124         "1758"
39125       ],
39126       [
39127         "Saint Martin (Saint-Martin (partie française))",
39128         "mf",
39129         "590",
39130         2
39131       ],
39132       [
39133         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39134         "pm",
39135         "508"
39136       ],
39137       [
39138         "Saint Vincent and the Grenadines",
39139         "vc",
39140         "1784"
39141       ],
39142       [
39143         "Samoa",
39144         "ws",
39145         "685"
39146       ],
39147       [
39148         "San Marino",
39149         "sm",
39150         "378"
39151       ],
39152       [
39153         "São Tomé and Príncipe (São Tomé e Príncipe)",
39154         "st",
39155         "239"
39156       ],
39157       [
39158         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39159         "sa",
39160         "966"
39161       ],
39162       [
39163         "Senegal (Sénégal)",
39164         "sn",
39165         "221"
39166       ],
39167       [
39168         "Serbia (Србија)",
39169         "rs",
39170         "381"
39171       ],
39172       [
39173         "Seychelles",
39174         "sc",
39175         "248"
39176       ],
39177       [
39178         "Sierra Leone",
39179         "sl",
39180         "232"
39181       ],
39182       [
39183         "Singapore",
39184         "sg",
39185         "65"
39186       ],
39187       [
39188         "Sint Maarten",
39189         "sx",
39190         "1721"
39191       ],
39192       [
39193         "Slovakia (Slovensko)",
39194         "sk",
39195         "421"
39196       ],
39197       [
39198         "Slovenia (Slovenija)",
39199         "si",
39200         "386"
39201       ],
39202       [
39203         "Solomon Islands",
39204         "sb",
39205         "677"
39206       ],
39207       [
39208         "Somalia (Soomaaliya)",
39209         "so",
39210         "252"
39211       ],
39212       [
39213         "South Africa",
39214         "za",
39215         "27"
39216       ],
39217       [
39218         "South Korea (대한민국)",
39219         "kr",
39220         "82"
39221       ],
39222       [
39223         "South Sudan (‫جنوب السودان‬‎)",
39224         "ss",
39225         "211"
39226       ],
39227       [
39228         "Spain (España)",
39229         "es",
39230         "34"
39231       ],
39232       [
39233         "Sri Lanka (ශ්‍රී ලංකාව)",
39234         "lk",
39235         "94"
39236       ],
39237       [
39238         "Sudan (‫السودان‬‎)",
39239         "sd",
39240         "249"
39241       ],
39242       [
39243         "Suriname",
39244         "sr",
39245         "597"
39246       ],
39247       [
39248         "Svalbard and Jan Mayen",
39249         "sj",
39250         "47",
39251         1
39252       ],
39253       [
39254         "Swaziland",
39255         "sz",
39256         "268"
39257       ],
39258       [
39259         "Sweden (Sverige)",
39260         "se",
39261         "46"
39262       ],
39263       [
39264         "Switzerland (Schweiz)",
39265         "ch",
39266         "41"
39267       ],
39268       [
39269         "Syria (‫سوريا‬‎)",
39270         "sy",
39271         "963"
39272       ],
39273       [
39274         "Taiwan (台灣)",
39275         "tw",
39276         "886"
39277       ],
39278       [
39279         "Tajikistan",
39280         "tj",
39281         "992"
39282       ],
39283       [
39284         "Tanzania",
39285         "tz",
39286         "255"
39287       ],
39288       [
39289         "Thailand (ไทย)",
39290         "th",
39291         "66"
39292       ],
39293       [
39294         "Timor-Leste",
39295         "tl",
39296         "670"
39297       ],
39298       [
39299         "Togo",
39300         "tg",
39301         "228"
39302       ],
39303       [
39304         "Tokelau",
39305         "tk",
39306         "690"
39307       ],
39308       [
39309         "Tonga",
39310         "to",
39311         "676"
39312       ],
39313       [
39314         "Trinidad and Tobago",
39315         "tt",
39316         "1868"
39317       ],
39318       [
39319         "Tunisia (‫تونس‬‎)",
39320         "tn",
39321         "216"
39322       ],
39323       [
39324         "Turkey (Türkiye)",
39325         "tr",
39326         "90"
39327       ],
39328       [
39329         "Turkmenistan",
39330         "tm",
39331         "993"
39332       ],
39333       [
39334         "Turks and Caicos Islands",
39335         "tc",
39336         "1649"
39337       ],
39338       [
39339         "Tuvalu",
39340         "tv",
39341         "688"
39342       ],
39343       [
39344         "U.S. Virgin Islands",
39345         "vi",
39346         "1340"
39347       ],
39348       [
39349         "Uganda",
39350         "ug",
39351         "256"
39352       ],
39353       [
39354         "Ukraine (Україна)",
39355         "ua",
39356         "380"
39357       ],
39358       [
39359         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39360         "ae",
39361         "971"
39362       ],
39363       [
39364         "United Kingdom",
39365         "gb",
39366         "44",
39367         0
39368       ],
39369       [
39370         "United States",
39371         "us",
39372         "1",
39373         0
39374       ],
39375       [
39376         "Uruguay",
39377         "uy",
39378         "598"
39379       ],
39380       [
39381         "Uzbekistan (Oʻzbekiston)",
39382         "uz",
39383         "998"
39384       ],
39385       [
39386         "Vanuatu",
39387         "vu",
39388         "678"
39389       ],
39390       [
39391         "Vatican City (Città del Vaticano)",
39392         "va",
39393         "39",
39394         1
39395       ],
39396       [
39397         "Venezuela",
39398         "ve",
39399         "58"
39400       ],
39401       [
39402         "Vietnam (Việt Nam)",
39403         "vn",
39404         "84"
39405       ],
39406       [
39407         "Wallis and Futuna (Wallis-et-Futuna)",
39408         "wf",
39409         "681"
39410       ],
39411       [
39412         "Western Sahara (‫الصحراء الغربية‬‎)",
39413         "eh",
39414         "212",
39415         1
39416       ],
39417       [
39418         "Yemen (‫اليمن‬‎)",
39419         "ye",
39420         "967"
39421       ],
39422       [
39423         "Zambia",
39424         "zm",
39425         "260"
39426       ],
39427       [
39428         "Zimbabwe",
39429         "zw",
39430         "263"
39431       ],
39432       [
39433         "Åland Islands",
39434         "ax",
39435         "358",
39436         1
39437       ]
39438   ];
39439   
39440   return d;
39441 }/**
39442 *    This script refer to:
39443 *    Title: International Telephone Input
39444 *    Author: Jack O'Connor
39445 *    Code version:  v12.1.12
39446 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39447 **/
39448
39449 /**
39450  * @class Roo.bootstrap.PhoneInput
39451  * @extends Roo.bootstrap.TriggerField
39452  * An input with International dial-code selection
39453  
39454  * @cfg {String} defaultDialCode default '+852'
39455  * @cfg {Array} preferedCountries default []
39456   
39457  * @constructor
39458  * Create a new PhoneInput.
39459  * @param {Object} config Configuration options
39460  */
39461
39462 Roo.bootstrap.PhoneInput = function(config) {
39463     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39464 };
39465
39466 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39467         
39468         listWidth: undefined,
39469         
39470         selectedClass: 'active',
39471         
39472         invalidClass : "has-warning",
39473         
39474         validClass: 'has-success',
39475         
39476         allowed: '0123456789',
39477         
39478         /**
39479          * @cfg {String} defaultDialCode The default dial code when initializing the input
39480          */
39481         defaultDialCode: '+852',
39482         
39483         /**
39484          * @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
39485          */
39486         preferedCountries: false,
39487         
39488         getAutoCreate : function()
39489         {
39490             var data = Roo.bootstrap.PhoneInputData();
39491             var align = this.labelAlign || this.parentLabelAlign();
39492             var id = Roo.id();
39493             
39494             this.allCountries = [];
39495             this.dialCodeMapping = [];
39496             
39497             for (var i = 0; i < data.length; i++) {
39498               var c = data[i];
39499               this.allCountries[i] = {
39500                 name: c[0],
39501                 iso2: c[1],
39502                 dialCode: c[2],
39503                 priority: c[3] || 0,
39504                 areaCodes: c[4] || null
39505               };
39506               this.dialCodeMapping[c[2]] = {
39507                   name: c[0],
39508                   iso2: c[1],
39509                   priority: c[3] || 0,
39510                   areaCodes: c[4] || null
39511               };
39512             }
39513             
39514             var cfg = {
39515                 cls: 'form-group',
39516                 cn: []
39517             };
39518             
39519             var input =  {
39520                 tag: 'input',
39521                 id : id,
39522                 cls : 'form-control tel-input',
39523                 autocomplete: 'new-password'
39524             };
39525             
39526             var hiddenInput = {
39527                 tag: 'input',
39528                 type: 'hidden',
39529                 cls: 'hidden-tel-input'
39530             };
39531             
39532             if (this.name) {
39533                 hiddenInput.name = this.name;
39534             }
39535             
39536             if (this.disabled) {
39537                 input.disabled = true;
39538             }
39539             
39540             var flag_container = {
39541                 tag: 'div',
39542                 cls: 'flag-box',
39543                 cn: [
39544                     {
39545                         tag: 'div',
39546                         cls: 'flag'
39547                     },
39548                     {
39549                         tag: 'div',
39550                         cls: 'caret'
39551                     }
39552                 ]
39553             };
39554             
39555             var box = {
39556                 tag: 'div',
39557                 cls: this.hasFeedback ? 'has-feedback' : '',
39558                 cn: [
39559                     hiddenInput,
39560                     input,
39561                     {
39562                         tag: 'input',
39563                         cls: 'dial-code-holder',
39564                         disabled: true
39565                     }
39566                 ]
39567             };
39568             
39569             var container = {
39570                 cls: 'roo-select2-container input-group',
39571                 cn: [
39572                     flag_container,
39573                     box
39574                 ]
39575             };
39576             
39577             if (this.fieldLabel.length) {
39578                 var indicator = {
39579                     tag: 'i',
39580                     tooltip: 'This field is required'
39581                 };
39582                 
39583                 var label = {
39584                     tag: 'label',
39585                     'for':  id,
39586                     cls: 'control-label',
39587                     cn: []
39588                 };
39589                 
39590                 var label_text = {
39591                     tag: 'span',
39592                     html: this.fieldLabel
39593                 };
39594                 
39595                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39596                 label.cn = [
39597                     indicator,
39598                     label_text
39599                 ];
39600                 
39601                 if(this.indicatorpos == 'right') {
39602                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39603                     label.cn = [
39604                         label_text,
39605                         indicator
39606                     ];
39607                 }
39608                 
39609                 if(align == 'left') {
39610                     container = {
39611                         tag: 'div',
39612                         cn: [
39613                             container
39614                         ]
39615                     };
39616                     
39617                     if(this.labelWidth > 12){
39618                         label.style = "width: " + this.labelWidth + 'px';
39619                     }
39620                     if(this.labelWidth < 13 && this.labelmd == 0){
39621                         this.labelmd = this.labelWidth;
39622                     }
39623                     if(this.labellg > 0){
39624                         label.cls += ' col-lg-' + this.labellg;
39625                         input.cls += ' col-lg-' + (12 - this.labellg);
39626                     }
39627                     if(this.labelmd > 0){
39628                         label.cls += ' col-md-' + this.labelmd;
39629                         container.cls += ' col-md-' + (12 - this.labelmd);
39630                     }
39631                     if(this.labelsm > 0){
39632                         label.cls += ' col-sm-' + this.labelsm;
39633                         container.cls += ' col-sm-' + (12 - this.labelsm);
39634                     }
39635                     if(this.labelxs > 0){
39636                         label.cls += ' col-xs-' + this.labelxs;
39637                         container.cls += ' col-xs-' + (12 - this.labelxs);
39638                     }
39639                 }
39640             }
39641             
39642             cfg.cn = [
39643                 label,
39644                 container
39645             ];
39646             
39647             var settings = this;
39648             
39649             ['xs','sm','md','lg'].map(function(size){
39650                 if (settings[size]) {
39651                     cfg.cls += ' col-' + size + '-' + settings[size];
39652                 }
39653             });
39654             
39655             this.store = new Roo.data.Store({
39656                 proxy : new Roo.data.MemoryProxy({}),
39657                 reader : new Roo.data.JsonReader({
39658                     fields : [
39659                         {
39660                             'name' : 'name',
39661                             'type' : 'string'
39662                         },
39663                         {
39664                             'name' : 'iso2',
39665                             'type' : 'string'
39666                         },
39667                         {
39668                             'name' : 'dialCode',
39669                             'type' : 'string'
39670                         },
39671                         {
39672                             'name' : 'priority',
39673                             'type' : 'string'
39674                         },
39675                         {
39676                             'name' : 'areaCodes',
39677                             'type' : 'string'
39678                         }
39679                     ]
39680                 })
39681             });
39682             
39683             if(!this.preferedCountries) {
39684                 this.preferedCountries = [
39685                     'hk',
39686                     'gb',
39687                     'us'
39688                 ];
39689             }
39690             
39691             var p = this.preferedCountries.reverse();
39692             
39693             if(p) {
39694                 for (var i = 0; i < p.length; i++) {
39695                     for (var j = 0; j < this.allCountries.length; j++) {
39696                         if(this.allCountries[j].iso2 == p[i]) {
39697                             var t = this.allCountries[j];
39698                             this.allCountries.splice(j,1);
39699                             this.allCountries.unshift(t);
39700                         }
39701                     } 
39702                 }
39703             }
39704             
39705             this.store.proxy.data = {
39706                 success: true,
39707                 data: this.allCountries
39708             };
39709             
39710             return cfg;
39711         },
39712         
39713         initEvents : function()
39714         {
39715             this.createList();
39716             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39717             
39718             this.indicator = this.indicatorEl();
39719             this.flag = this.flagEl();
39720             this.dialCodeHolder = this.dialCodeHolderEl();
39721             
39722             this.trigger = this.el.select('div.flag-box',true).first();
39723             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39724             
39725             var _this = this;
39726             
39727             (function(){
39728                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39729                 _this.list.setWidth(lw);
39730             }).defer(100);
39731             
39732             this.list.on('mouseover', this.onViewOver, this);
39733             this.list.on('mousemove', this.onViewMove, this);
39734             this.inputEl().on("keyup", this.onKeyUp, this);
39735             
39736             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39737
39738             this.view = new Roo.View(this.list, this.tpl, {
39739                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39740             });
39741             
39742             this.view.on('click', this.onViewClick, this);
39743             this.setValue(this.defaultDialCode);
39744         },
39745         
39746         onTriggerClick : function(e)
39747         {
39748             Roo.log('trigger click');
39749             if(this.disabled){
39750                 return;
39751             }
39752             
39753             if(this.isExpanded()){
39754                 this.collapse();
39755                 this.hasFocus = false;
39756             }else {
39757                 this.store.load({});
39758                 this.hasFocus = true;
39759                 this.expand();
39760             }
39761         },
39762         
39763         isExpanded : function()
39764         {
39765             return this.list.isVisible();
39766         },
39767         
39768         collapse : function()
39769         {
39770             if(!this.isExpanded()){
39771                 return;
39772             }
39773             this.list.hide();
39774             Roo.get(document).un('mousedown', this.collapseIf, this);
39775             Roo.get(document).un('mousewheel', this.collapseIf, this);
39776             this.fireEvent('collapse', this);
39777             this.validate();
39778         },
39779         
39780         expand : function()
39781         {
39782             Roo.log('expand');
39783
39784             if(this.isExpanded() || !this.hasFocus){
39785                 return;
39786             }
39787             
39788             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39789             this.list.setWidth(lw);
39790             
39791             this.list.show();
39792             this.restrictHeight();
39793             
39794             Roo.get(document).on('mousedown', this.collapseIf, this);
39795             Roo.get(document).on('mousewheel', this.collapseIf, this);
39796             
39797             this.fireEvent('expand', this);
39798         },
39799         
39800         restrictHeight : function()
39801         {
39802             this.list.alignTo(this.inputEl(), this.listAlign);
39803             this.list.alignTo(this.inputEl(), this.listAlign);
39804         },
39805         
39806         onViewOver : function(e, t)
39807         {
39808             if(this.inKeyMode){
39809                 return;
39810             }
39811             var item = this.view.findItemFromChild(t);
39812             
39813             if(item){
39814                 var index = this.view.indexOf(item);
39815                 this.select(index, false);
39816             }
39817         },
39818
39819         // private
39820         onViewClick : function(view, doFocus, el, e)
39821         {
39822             var index = this.view.getSelectedIndexes()[0];
39823             
39824             var r = this.store.getAt(index);
39825             
39826             if(r){
39827                 this.onSelect(r, index);
39828             }
39829             if(doFocus !== false && !this.blockFocus){
39830                 this.inputEl().focus();
39831             }
39832         },
39833         
39834         onViewMove : function(e, t)
39835         {
39836             this.inKeyMode = false;
39837         },
39838         
39839         select : function(index, scrollIntoView)
39840         {
39841             this.selectedIndex = index;
39842             this.view.select(index);
39843             if(scrollIntoView !== false){
39844                 var el = this.view.getNode(index);
39845                 if(el){
39846                     this.list.scrollChildIntoView(el, false);
39847                 }
39848             }
39849         },
39850         
39851         createList : function()
39852         {
39853             this.list = Roo.get(document.body).createChild({
39854                 tag: 'ul',
39855                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39856                 style: 'display:none'
39857             });
39858             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39859         },
39860         
39861         collapseIf : function(e)
39862         {
39863             var in_combo  = e.within(this.el);
39864             var in_list =  e.within(this.list);
39865             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39866             
39867             if (in_combo || in_list || is_list) {
39868                 return;
39869             }
39870             this.collapse();
39871         },
39872         
39873         onSelect : function(record, index)
39874         {
39875             if(this.fireEvent('beforeselect', this, record, index) !== false){
39876                 
39877                 this.setFlagClass(record.data.iso2);
39878                 this.setDialCode(record.data.dialCode);
39879                 this.hasFocus = false;
39880                 this.collapse();
39881                 this.fireEvent('select', this, record, index);
39882             }
39883         },
39884         
39885         flagEl : function()
39886         {
39887             var flag = this.el.select('div.flag',true).first();
39888             if(!flag){
39889                 return false;
39890             }
39891             return flag;
39892         },
39893         
39894         dialCodeHolderEl : function()
39895         {
39896             var d = this.el.select('input.dial-code-holder',true).first();
39897             if(!d){
39898                 return false;
39899             }
39900             return d;
39901         },
39902         
39903         setDialCode : function(v)
39904         {
39905             this.dialCodeHolder.dom.value = '+'+v;
39906         },
39907         
39908         setFlagClass : function(n)
39909         {
39910             this.flag.dom.className = 'flag '+n;
39911         },
39912         
39913         getValue : function()
39914         {
39915             var v = this.inputEl().getValue();
39916             if(this.dialCodeHolder) {
39917                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39918             }
39919             return v;
39920         },
39921         
39922         setValue : function(v)
39923         {
39924             var d = this.getDialCode(v);
39925             
39926             //invalid dial code
39927             if(v.length == 0 || !d || d.length == 0) {
39928                 if(this.rendered){
39929                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39930                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39931                 }
39932                 return;
39933             }
39934             
39935             //valid dial code
39936             this.setFlagClass(this.dialCodeMapping[d].iso2);
39937             this.setDialCode(d);
39938             this.inputEl().dom.value = v.replace('+'+d,'');
39939             this.hiddenEl().dom.value = this.getValue();
39940             
39941             this.validate();
39942         },
39943         
39944         getDialCode : function(v = '')
39945         {
39946             if (v.length == 0) {
39947                 return this.dialCodeHolder.dom.value;
39948             }
39949             
39950             var dialCode = "";
39951             if (v.charAt(0) != "+") {
39952                 return false;
39953             }
39954             var numericChars = "";
39955             for (var i = 1; i < v.length; i++) {
39956               var c = v.charAt(i);
39957               if (!isNaN(c)) {
39958                 numericChars += c;
39959                 if (this.dialCodeMapping[numericChars]) {
39960                   dialCode = v.substr(1, i);
39961                 }
39962                 if (numericChars.length == 4) {
39963                   break;
39964                 }
39965               }
39966             }
39967             return dialCode;
39968         },
39969         
39970         reset : function()
39971         {
39972             this.setValue(this.defaultDialCode);
39973             this.validate();
39974         },
39975         
39976         hiddenEl : function()
39977         {
39978             return this.el.select('input.hidden-tel-input',true).first();
39979         },
39980         
39981         onKeyUp : function(e){
39982             
39983             var k = e.getKey();
39984             var c = e.getCharCode();
39985             
39986             if(
39987                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39988                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39989             ){
39990                 e.stopEvent();
39991             }
39992             
39993             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39994             //     return;
39995             // }
39996             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39997                 e.stopEvent();
39998             }
39999             
40000             this.setValue(this.getValue());
40001         }
40002         
40003 });
40004 /**
40005  * @class Roo.bootstrap.MoneyField
40006  * @extends Roo.bootstrap.ComboBox
40007  * Bootstrap MoneyField class
40008  * 
40009  * @constructor
40010  * Create a new MoneyField.
40011  * @param {Object} config Configuration options
40012  */
40013
40014 Roo.bootstrap.MoneyField = function(config) {
40015     
40016     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40017     
40018 };
40019
40020 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40021     
40022     /**
40023      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40024      */
40025     allowDecimals : true,
40026     /**
40027      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40028      */
40029     decimalSeparator : ".",
40030     /**
40031      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40032      */
40033     decimalPrecision : 0,
40034     /**
40035      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40036      */
40037     allowNegative : true,
40038     /**
40039      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40040      */
40041     minValue : Number.NEGATIVE_INFINITY,
40042     /**
40043      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40044      */
40045     maxValue : Number.MAX_VALUE,
40046     /**
40047      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40048      */
40049     minText : "The minimum value for this field is {0}",
40050     /**
40051      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40052      */
40053     maxText : "The maximum value for this field is {0}",
40054     /**
40055      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40056      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40057      */
40058     nanText : "{0} is not a valid number",
40059     /**
40060      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40061      */
40062     castInt : true,
40063     /**
40064      * @cfg {String} defaults currency of the MoneyField
40065      * value should be in lkey
40066      */
40067     defaultCurrency : false,
40068     /**
40069      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40070      */
40071     thousandsDelimiter : false,
40072     
40073     
40074     inputlg : 9,
40075     inputmd : 9,
40076     inputsm : 9,
40077     inputxs : 6,
40078     
40079     store : false,
40080     
40081     getAutoCreate : function()
40082     {
40083         var align = this.labelAlign || this.parentLabelAlign();
40084         
40085         var id = Roo.id();
40086
40087         var cfg = {
40088             cls: 'form-group',
40089             cn: []
40090         };
40091
40092         var input =  {
40093             tag: 'input',
40094             id : id,
40095             cls : 'form-control roo-money-amount-input',
40096             autocomplete: 'new-password'
40097         };
40098         
40099         var hiddenInput = {
40100             tag: 'input',
40101             type: 'hidden',
40102             id: Roo.id(),
40103             cls: 'hidden-number-input'
40104         };
40105         
40106         if (this.name) {
40107             hiddenInput.name = this.name;
40108         }
40109
40110         if (this.disabled) {
40111             input.disabled = true;
40112         }
40113
40114         var clg = 12 - this.inputlg;
40115         var cmd = 12 - this.inputmd;
40116         var csm = 12 - this.inputsm;
40117         var cxs = 12 - this.inputxs;
40118         
40119         var container = {
40120             tag : 'div',
40121             cls : 'row roo-money-field',
40122             cn : [
40123                 {
40124                     tag : 'div',
40125                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40126                     cn : [
40127                         {
40128                             tag : 'div',
40129                             cls: 'roo-select2-container input-group',
40130                             cn: [
40131                                 {
40132                                     tag : 'input',
40133                                     cls : 'form-control roo-money-currency-input',
40134                                     autocomplete: 'new-password',
40135                                     readOnly : 1,
40136                                     name : this.currencyName
40137                                 },
40138                                 {
40139                                     tag :'span',
40140                                     cls : 'input-group-addon',
40141                                     cn : [
40142                                         {
40143                                             tag: 'span',
40144                                             cls: 'caret'
40145                                         }
40146                                     ]
40147                                 }
40148                             ]
40149                         }
40150                     ]
40151                 },
40152                 {
40153                     tag : 'div',
40154                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40155                     cn : [
40156                         {
40157                             tag: 'div',
40158                             cls: this.hasFeedback ? 'has-feedback' : '',
40159                             cn: [
40160                                 input
40161                             ]
40162                         }
40163                     ]
40164                 }
40165             ]
40166             
40167         };
40168         
40169         if (this.fieldLabel.length) {
40170             var indicator = {
40171                 tag: 'i',
40172                 tooltip: 'This field is required'
40173             };
40174
40175             var label = {
40176                 tag: 'label',
40177                 'for':  id,
40178                 cls: 'control-label',
40179                 cn: []
40180             };
40181
40182             var label_text = {
40183                 tag: 'span',
40184                 html: this.fieldLabel
40185             };
40186
40187             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40188             label.cn = [
40189                 indicator,
40190                 label_text
40191             ];
40192
40193             if(this.indicatorpos == 'right') {
40194                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40195                 label.cn = [
40196                     label_text,
40197                     indicator
40198                 ];
40199             }
40200
40201             if(align == 'left') {
40202                 container = {
40203                     tag: 'div',
40204                     cn: [
40205                         container
40206                     ]
40207                 };
40208
40209                 if(this.labelWidth > 12){
40210                     label.style = "width: " + this.labelWidth + 'px';
40211                 }
40212                 if(this.labelWidth < 13 && this.labelmd == 0){
40213                     this.labelmd = this.labelWidth;
40214                 }
40215                 if(this.labellg > 0){
40216                     label.cls += ' col-lg-' + this.labellg;
40217                     input.cls += ' col-lg-' + (12 - this.labellg);
40218                 }
40219                 if(this.labelmd > 0){
40220                     label.cls += ' col-md-' + this.labelmd;
40221                     container.cls += ' col-md-' + (12 - this.labelmd);
40222                 }
40223                 if(this.labelsm > 0){
40224                     label.cls += ' col-sm-' + this.labelsm;
40225                     container.cls += ' col-sm-' + (12 - this.labelsm);
40226                 }
40227                 if(this.labelxs > 0){
40228                     label.cls += ' col-xs-' + this.labelxs;
40229                     container.cls += ' col-xs-' + (12 - this.labelxs);
40230                 }
40231             }
40232         }
40233
40234         cfg.cn = [
40235             label,
40236             container,
40237             hiddenInput
40238         ];
40239         
40240         var settings = this;
40241
40242         ['xs','sm','md','lg'].map(function(size){
40243             if (settings[size]) {
40244                 cfg.cls += ' col-' + size + '-' + settings[size];
40245             }
40246         });
40247         
40248         return cfg;
40249     },
40250     
40251     initEvents : function()
40252     {
40253         this.indicator = this.indicatorEl();
40254         
40255         this.initCurrencyEvent();
40256         
40257         this.initNumberEvent();
40258     },
40259     
40260     initCurrencyEvent : function()
40261     {
40262         if (!this.store) {
40263             throw "can not find store for combo";
40264         }
40265         
40266         this.store = Roo.factory(this.store, Roo.data);
40267         this.store.parent = this;
40268         
40269         this.createList();
40270         
40271         this.triggerEl = this.el.select('.input-group-addon', true).first();
40272         
40273         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40274         
40275         var _this = this;
40276         
40277         (function(){
40278             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40279             _this.list.setWidth(lw);
40280         }).defer(100);
40281         
40282         this.list.on('mouseover', this.onViewOver, this);
40283         this.list.on('mousemove', this.onViewMove, this);
40284         this.list.on('scroll', this.onViewScroll, this);
40285         
40286         if(!this.tpl){
40287             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40288         }
40289         
40290         this.view = new Roo.View(this.list, this.tpl, {
40291             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40292         });
40293         
40294         this.view.on('click', this.onViewClick, this);
40295         
40296         this.store.on('beforeload', this.onBeforeLoad, this);
40297         this.store.on('load', this.onLoad, this);
40298         this.store.on('loadexception', this.onLoadException, this);
40299         
40300         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40301             "up" : function(e){
40302                 this.inKeyMode = true;
40303                 this.selectPrev();
40304             },
40305
40306             "down" : function(e){
40307                 if(!this.isExpanded()){
40308                     this.onTriggerClick();
40309                 }else{
40310                     this.inKeyMode = true;
40311                     this.selectNext();
40312                 }
40313             },
40314
40315             "enter" : function(e){
40316                 this.collapse();
40317                 
40318                 if(this.fireEvent("specialkey", this, e)){
40319                     this.onViewClick(false);
40320                 }
40321                 
40322                 return true;
40323             },
40324
40325             "esc" : function(e){
40326                 this.collapse();
40327             },
40328
40329             "tab" : function(e){
40330                 this.collapse();
40331                 
40332                 if(this.fireEvent("specialkey", this, e)){
40333                     this.onViewClick(false);
40334                 }
40335                 
40336                 return true;
40337             },
40338
40339             scope : this,
40340
40341             doRelay : function(foo, bar, hname){
40342                 if(hname == 'down' || this.scope.isExpanded()){
40343                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40344                 }
40345                 return true;
40346             },
40347
40348             forceKeyDown: true
40349         });
40350         
40351         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40352         
40353     },
40354     
40355     initNumberEvent : function(e)
40356     {
40357         this.inputEl().on("keydown" , this.fireKey,  this);
40358         this.inputEl().on("focus", this.onFocus,  this);
40359         this.inputEl().on("blur", this.onBlur,  this);
40360         
40361         this.inputEl().relayEvent('keyup', this);
40362         
40363         if(this.indicator){
40364             this.indicator.addClass('invisible');
40365         }
40366  
40367         this.originalValue = this.getValue();
40368         
40369         if(this.validationEvent == 'keyup'){
40370             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40371             this.inputEl().on('keyup', this.filterValidation, this);
40372         }
40373         else if(this.validationEvent !== false){
40374             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40375         }
40376         
40377         if(this.selectOnFocus){
40378             this.on("focus", this.preFocus, this);
40379             
40380         }
40381         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40382             this.inputEl().on("keypress", this.filterKeys, this);
40383         } else {
40384             this.inputEl().relayEvent('keypress', this);
40385         }
40386         
40387         var allowed = "0123456789";
40388         
40389         if(this.allowDecimals){
40390             allowed += this.decimalSeparator;
40391         }
40392         
40393         if(this.allowNegative){
40394             allowed += "-";
40395         }
40396         
40397         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40398         
40399         var keyPress = function(e){
40400             
40401             var k = e.getKey();
40402             
40403             var c = e.getCharCode();
40404             
40405             if(
40406                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40407                     allowed.indexOf(String.fromCharCode(c)) === -1
40408             ){
40409                 e.stopEvent();
40410                 return;
40411             }
40412             
40413             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40414                 return;
40415             }
40416             
40417             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40418                 e.stopEvent();
40419             }
40420         };
40421         
40422         this.inputEl().on("keypress", keyPress, this);
40423         
40424     },
40425     
40426     onTriggerClick : function(e)
40427     {   
40428         if(this.disabled){
40429             return;
40430         }
40431         
40432         this.page = 0;
40433         this.loadNext = false;
40434         
40435         if(this.isExpanded()){
40436             this.collapse();
40437             return;
40438         }
40439         
40440         this.hasFocus = true;
40441         
40442         if(this.triggerAction == 'all') {
40443             this.doQuery(this.allQuery, true);
40444             return;
40445         }
40446         
40447         this.doQuery(this.getRawValue());
40448     },
40449     
40450     getCurrency : function()
40451     {   
40452         var v = this.currencyEl().getValue();
40453         
40454         return v;
40455     },
40456     
40457     restrictHeight : function()
40458     {
40459         this.list.alignTo(this.currencyEl(), this.listAlign);
40460         this.list.alignTo(this.currencyEl(), this.listAlign);
40461     },
40462     
40463     onViewClick : function(view, doFocus, el, e)
40464     {
40465         var index = this.view.getSelectedIndexes()[0];
40466         
40467         var r = this.store.getAt(index);
40468         
40469         if(r){
40470             this.onSelect(r, index);
40471         }
40472     },
40473     
40474     onSelect : function(record, index){
40475         
40476         if(this.fireEvent('beforeselect', this, record, index) !== false){
40477         
40478             this.setFromCurrencyData(index > -1 ? record.data : false);
40479             
40480             this.collapse();
40481             
40482             this.fireEvent('select', this, record, index);
40483         }
40484     },
40485     
40486     setFromCurrencyData : function(o)
40487     {
40488         var currency = '';
40489         
40490         this.lastCurrency = o;
40491         
40492         if (this.currencyField) {
40493             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40494         } else {
40495             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40496         }
40497         
40498         this.lastSelectionText = currency;
40499         
40500         //setting default currency
40501         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40502             this.setCurrency(this.defaultCurrency);
40503             return;
40504         }
40505         
40506         this.setCurrency(currency);
40507     },
40508     
40509     setFromData : function(o)
40510     {
40511         var c = {};
40512         
40513         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40514         
40515         this.setFromCurrencyData(c);
40516         
40517         var value = '';
40518         
40519         if (this.name) {
40520             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40521         } else {
40522             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40523         }
40524         
40525         this.setValue(value);
40526         
40527     },
40528     
40529     setCurrency : function(v)
40530     {   
40531         this.currencyValue = v;
40532         
40533         if(this.rendered){
40534             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40535             this.validate();
40536         }
40537     },
40538     
40539     setValue : function(v)
40540     {
40541         v = this.fixPrecision(v);
40542         
40543         v = String(v).replace(".", this.decimalSeparator);
40544         
40545         this.value = v;
40546         
40547         if(this.rendered){
40548             
40549             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40550             
40551             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40552                 this.thousandsDelimiter || ','
40553             );
40554             
40555             if(this.allowBlank && !v) {
40556                 this.inputEl().dom.value = '';
40557             }
40558             
40559             this.validate();
40560         }
40561     },
40562     
40563     getRawValue : function()
40564     {
40565         var v = this.inputEl().getValue();
40566         
40567         return v;
40568     },
40569     
40570     getValue : function()
40571     {
40572         return this.fixPrecision(this.parseValue(this.getRawValue()));
40573     },
40574     
40575     parseValue : function(value)
40576     {
40577         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40578         return isNaN(value) ? '' : value;
40579     },
40580     
40581     fixPrecision : function(value)
40582     {
40583         var nan = isNaN(value);
40584         
40585         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40586             return nan ? '' : value;
40587         }
40588         
40589         return parseFloat(value).toFixed(this.decimalPrecision);
40590     },
40591     
40592     decimalPrecisionFcn : function(v)
40593     {
40594         return Math.floor(v);
40595     },
40596     
40597     validateValue : function(value)
40598     {
40599         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40600             return false;
40601         }
40602         
40603         var num = this.parseValue(value);
40604         
40605         if(isNaN(num)){
40606             this.markInvalid(String.format(this.nanText, value));
40607             return false;
40608         }
40609         
40610         if(num < this.minValue){
40611             this.markInvalid(String.format(this.minText, this.minValue));
40612             return false;
40613         }
40614         
40615         if(num > this.maxValue){
40616             this.markInvalid(String.format(this.maxText, this.maxValue));
40617             return false;
40618         }
40619         
40620         return true;
40621     },
40622     
40623     validate : function()
40624     {
40625         if(this.disabled || this.allowBlank){
40626             this.markValid();
40627             return true;
40628         }
40629         
40630         var currency = this.getCurrency();
40631         
40632         if(this.validateValue(this.getRawValue()) && currency.length){
40633             this.markValid();
40634             return true;
40635         }
40636         
40637         this.markInvalid();
40638         return false;
40639     },
40640     
40641     getName: function()
40642     {
40643         return this.name;
40644     },
40645     
40646     beforeBlur : function()
40647     {
40648         if(!this.castInt){
40649             return;
40650         }
40651         
40652         var v = this.parseValue(this.getRawValue());
40653         
40654         if(v){
40655             this.setValue(v);
40656         }
40657     },
40658     
40659     onBlur : function()
40660     {
40661         this.beforeBlur();
40662         
40663         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40664             //this.el.removeClass(this.focusClass);
40665         }
40666         
40667         this.hasFocus = false;
40668         
40669         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40670             this.validate();
40671         }
40672         
40673         var v = this.getValue();
40674         
40675         if(String(v) !== String(this.startValue)){
40676             this.fireEvent('change', this, v, this.startValue);
40677         }
40678         
40679         this.fireEvent("blur", this);
40680     },
40681     
40682     inputEl : function()
40683     {
40684         return this.el.select('.roo-money-amount-input', true).first();
40685     },
40686     
40687     currencyEl : function()
40688     {
40689         return this.el.select('.roo-money-currency-input', true).first();
40690     },
40691     
40692     hiddenEl : function()
40693     {
40694         return this.el.select('input.hidden-number-input',true).first();
40695     }
40696     
40697 });