Roo/bootstrap/PagingToolbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244     
245         
246         skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249             
250             // if parent was disabled, then do not try and create the children..
251             if(!this[cntr](true)){
252                 tree.items = [];
253                 return tree;
254             }
255            
256             cn = Roo.factory(tree);
257            
258             cn.parentType = this.xtype; //??
259             cn.parentId = this.id;
260             
261             var build_from_html =  Roo.XComponent.build_from_html;
262             
263             
264             // does the container contain child eleemnts with 'xtype' attributes.
265             // that match this xtype..
266             // note - when we render we create these as well..
267             // so we should check to see if body has xtype set.
268             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
269                
270                 var self_cntr_el = Roo.get(this[cntr](false));
271                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
272                 if (echild) { 
273                     //Roo.log(Roo.XComponent.build_from_html);
274                     //Roo.log("got echild:");
275                     //Roo.log(echild);
276                 }
277                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
278                 // and are not displayed -this causes this to use up the wrong element when matching.
279                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
280                 
281                 
282                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
284                   
285                   
286                   
287                     cn.el = echild;
288                   //  Roo.log("GOT");
289                     //echild.dom.removeAttribute('xtype');
290                 } else {
291                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
292                     Roo.debug && Roo.log(self_cntr_el);
293                     Roo.debug && Roo.log(echild);
294                     Roo.debug && Roo.log(cn);
295                 }
296             }
297            
298             
299            
300             // if object has flexy:if - then it may or may not be rendered.
301             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
302                 // skip a flexy if element.
303                 Roo.debug && Roo.log('skipping render');
304                 Roo.debug && Roo.log(tree);
305                 if (!cn.el) {
306                     Roo.debug && Roo.log('skipping all children');
307                     skip_children = true;
308                 }
309                 
310              } else {
311                  
312                 // actually if flexy:foreach is found, we really want to create 
313                 // multiple copies here...
314                 //Roo.log('render');
315                 //Roo.log(this[cntr]());
316                 // some elements do not have render methods.. like the layouts...
317                 /*
318                 if(this[cntr](true) === false){
319                     cn.items = [];
320                     return cn;
321                 }
322                 */
323                 cn.render && cn.render(this[cntr](true));
324                 
325              }
326             // then add the element..
327         }
328          
329         // handle the kids..
330         
331         var nitems = [];
332         /*
333         if (typeof (tree.menu) != 'undefined') {
334             tree.menu.parentType = cn.xtype;
335             tree.menu.triggerEl = cn.el;
336             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
337             
338         }
339         */
340         if (!tree.items || !tree.items.length) {
341             cn.items = nitems;
342             //Roo.log(["no children", this]);
343             
344             return cn;
345         }
346          
347         var items = tree.items;
348         delete tree.items;
349         
350         //Roo.log(items.length);
351             // add the items..
352         if (!skip_children) {    
353             for(var i =0;i < items.length;i++) {
354               //  Roo.log(['add child', items[i]]);
355                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356             }
357         }
358         
359         cn.items = nitems;
360         
361         //Roo.log("fire childrenrendered");
362         
363         cn.fireEvent('childrenrendered', this);
364         
365         return cn;
366     },
367     /**
368      * Show a component - removes 'hidden' class
369      */
370     show : function()
371     {
372         if (this.el) {
373             this.el.removeClass('hidden');
374         }
375     },
376     /**
377      * Hide a component - adds 'hidden' class
378      */
379     hide: function()
380     {
381         if (this.el && !this.el.hasClass('hidden')) {
382             this.el.addClass('hidden');
383         }
384     }
385 });
386
387  /*
388  * - LGPL
389  *
390  * Body
391  *
392  */
393
394 /**
395  * @class Roo.bootstrap.Body
396  * @extends Roo.bootstrap.Component
397  * Bootstrap Body class
398  *
399  * @constructor
400  * Create a new body
401  * @param {Object} config The config object
402  */
403
404 Roo.bootstrap.Body = function(config){
405
406     config = config || {};
407
408     Roo.bootstrap.Body.superclass.constructor.call(this, config);
409     this.el = Roo.get(config.el ? config.el : document.body );
410     if (this.cls && this.cls.length) {
411         Roo.get(document.body).addClass(this.cls);
412     }
413 };
414
415 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
416
417     is_body : true,// just to make sure it's constructed?
418
419         autoCreate : {
420         cls: 'container'
421     },
422     onRender : function(ct, position)
423     {
424        /* Roo.log("Roo.bootstrap.Body - onRender");
425         if (this.cls && this.cls.length) {
426             Roo.get(document.body).addClass(this.cls);
427         }
428         // style??? xttr???
429         */
430     }
431
432
433
434
435 });
436 /*
437  * - LGPL
438  *
439  * button group
440  * 
441  */
442
443
444 /**
445  * @class Roo.bootstrap.ButtonGroup
446  * @extends Roo.bootstrap.Component
447  * Bootstrap ButtonGroup class
448  * @cfg {String} size lg | sm | xs (default empty normal)
449  * @cfg {String} align vertical | justified  (default none)
450  * @cfg {String} direction up | down (default down)
451  * @cfg {Boolean} toolbar false | true
452  * @cfg {Boolean} btn true | false
453  * 
454  * 
455  * @constructor
456  * Create a new Input
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.ButtonGroup = function(config){
461     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
462 };
463
464 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
465     
466     size: '',
467     align: '',
468     direction: '',
469     toolbar: false,
470     btn: true,
471
472     getAutoCreate : function(){
473         var cfg = {
474             cls: 'btn-group',
475             html : null
476         };
477         
478         cfg.html = this.html || cfg.html;
479         
480         if (this.toolbar) {
481             cfg = {
482                 cls: 'btn-toolbar',
483                 html: null
484             };
485             
486             return cfg;
487         }
488         
489         if (['vertical','justified'].indexOf(this.align)!==-1) {
490             cfg.cls = 'btn-group-' + this.align;
491             
492             if (this.align == 'justified') {
493                 console.log(this.items);
494             }
495         }
496         
497         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
498             cfg.cls += ' btn-group-' + this.size;
499         }
500         
501         if (this.direction == 'up') {
502             cfg.cls += ' dropup' ;
503         }
504         
505         return cfg;
506     }
507    
508 });
509
510  /*
511  * - LGPL
512  *
513  * button
514  * 
515  */
516
517 /**
518  * @class Roo.bootstrap.Button
519  * @extends Roo.bootstrap.Component
520  * Bootstrap Button class
521  * @cfg {String} html The button content
522  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
523  * @cfg {String} size ( lg | sm | xs)
524  * @cfg {String} tag ( a | input | submit)
525  * @cfg {String} href empty or href
526  * @cfg {Boolean} disabled default false;
527  * @cfg {Boolean} isClose default false;
528  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
529  * @cfg {String} badge text for badge
530  * @cfg {String} theme default 
531  * @cfg {Boolean} inverse 
532  * @cfg {Boolean} toggle 
533  * @cfg {String} ontext text for on toggle state
534  * @cfg {String} offtext text for off toggle state
535  * @cfg {Boolean} defaulton 
536  * @cfg {Boolean} preventDefault  default true
537  * @cfg {Boolean} removeClass remove the standard class..
538  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
539  * 
540  * @constructor
541  * Create a new button
542  * @param {Object} config The config object
543  */
544
545
546 Roo.bootstrap.Button = function(config){
547     Roo.bootstrap.Button.superclass.constructor.call(this, config);
548     this.weightClass = ["btn-default", 
549                        "btn-primary", 
550                        "btn-success", 
551                        "btn-info", 
552                        "btn-warning",
553                        "btn-danger",
554                        "btn-link"
555                       ],  
556     this.addEvents({
557         // raw events
558         /**
559          * @event click
560          * When a butotn is pressed
561          * @param {Roo.bootstrap.Button} this
562          * @param {Roo.EventObject} e
563          */
564         "click" : true,
565          /**
566          * @event toggle
567          * After the button has been toggles
568          * @param {Roo.EventObject} e
569          * @param {boolean} pressed (also available as button.pressed)
570          */
571         "toggle" : true
572     });
573 };
574
575 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
576     html: false,
577     active: false,
578     weight: '',
579     size: '',
580     tag: 'button',
581     href: '',
582     disabled: false,
583     isClose: false,
584     glyphicon: '',
585     badge: '',
586     theme: 'default',
587     inverse: false,
588     
589     toggle: false,
590     ontext: 'ON',
591     offtext: 'OFF',
592     defaulton: true,
593     preventDefault: true,
594     removeClass: false,
595     name: false,
596     target: false,
597     
598     
599     pressed : null,
600      
601     
602     getAutoCreate : function(){
603         
604         var cfg = {
605             tag : 'button',
606             cls : 'roo-button',
607             html: ''
608         };
609         
610         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
611             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
612             this.tag = 'button';
613         } else {
614             cfg.tag = this.tag;
615         }
616         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
617         
618         if (this.toggle == true) {
619             cfg={
620                 tag: 'div',
621                 cls: 'slider-frame roo-button',
622                 cn: [
623                     {
624                         tag: 'span',
625                         'data-on-text':'ON',
626                         'data-off-text':'OFF',
627                         cls: 'slider-button',
628                         html: this.offtext
629                     }
630                 ]
631             };
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 cfg.cls += ' '+this.weight;
635             }
636             
637             return cfg;
638         }
639         
640         if (this.isClose) {
641             cfg.cls += ' close';
642             
643             cfg["aria-hidden"] = true;
644             
645             cfg.html = "&times;";
646             
647             return cfg;
648         }
649         
650          
651         if (this.theme==='default') {
652             cfg.cls = 'btn roo-button';
653             
654             //if (this.parentType != 'Navbar') {
655             this.weight = this.weight.length ?  this.weight : 'default';
656             //}
657             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
658                 
659                 cfg.cls += ' btn-' + this.weight;
660             }
661         } else if (this.theme==='glow') {
662             
663             cfg.tag = 'a';
664             cfg.cls = 'btn-glow roo-button';
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 
668                 cfg.cls += ' ' + this.weight;
669             }
670         }
671    
672         
673         if (this.inverse) {
674             this.cls += ' inverse';
675         }
676         
677         
678         if (this.active) {
679             cfg.cls += ' active';
680         }
681         
682         if (this.disabled) {
683             cfg.disabled = 'disabled';
684         }
685         
686         if (this.items) {
687             Roo.log('changing to ul' );
688             cfg.tag = 'ul';
689             this.glyphicon = 'caret';
690         }
691         
692         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
693          
694         //gsRoo.log(this.parentType);
695         if (this.parentType === 'Navbar' && !this.parent().bar) {
696             Roo.log('changing to li?');
697             
698             cfg.tag = 'li';
699             
700             cfg.cls = '';
701             cfg.cn =  [{
702                 tag : 'a',
703                 cls : 'roo-button',
704                 html : this.html,
705                 href : this.href || '#'
706             }];
707             if (this.menu) {
708                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
709                 cfg.cls += ' dropdown';
710             }   
711             
712             delete cfg.html;
713             
714         }
715         
716        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
717         
718         if (this.glyphicon) {
719             cfg.html = ' ' + cfg.html;
720             
721             cfg.cn = [
722                 {
723                     tag: 'span',
724                     cls: 'glyphicon glyphicon-' + this.glyphicon
725                 }
726             ];
727         }
728         
729         if (this.badge) {
730             cfg.html += ' ';
731             
732             cfg.tag = 'a';
733             
734 //            cfg.cls='btn roo-button';
735             
736             cfg.href=this.href;
737             
738             var value = cfg.html;
739             
740             if(this.glyphicon){
741                 value = {
742                             tag: 'span',
743                             cls: 'glyphicon glyphicon-' + this.glyphicon,
744                             html: this.html
745                         };
746                 
747             }
748             
749             cfg.cn = [
750                 value,
751                 {
752                     tag: 'span',
753                     cls: 'badge',
754                     html: this.badge
755                 }
756             ];
757             
758             cfg.html='';
759         }
760         
761         if (this.menu) {
762             cfg.cls += ' dropdown';
763             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
764         }
765         
766         if (cfg.tag !== 'a' && this.href !== '') {
767             throw "Tag must be a to set href.";
768         } else if (this.href.length > 0) {
769             cfg.href = this.href;
770         }
771         
772         if(this.removeClass){
773             cfg.cls = '';
774         }
775         
776         if(this.target){
777             cfg.target = this.target;
778         }
779         
780         return cfg;
781     },
782     initEvents: function() {
783        // Roo.log('init events?');
784 //        Roo.log(this.el.dom);
785         // add the menu...
786         
787         if (typeof (this.menu) != 'undefined') {
788             this.menu.parentType = this.xtype;
789             this.menu.triggerEl = this.el;
790             this.addxtype(Roo.apply({}, this.menu));
791         }
792
793
794        if (this.el.hasClass('roo-button')) {
795             this.el.on('click', this.onClick, this);
796        } else {
797             this.el.select('.roo-button').on('click', this.onClick, this);
798        }
799        
800        if(this.removeClass){
801            this.el.on('click', this.onClick, this);
802        }
803        
804        this.el.enableDisplayMode();
805         
806     },
807     onClick : function(e)
808     {
809         if (this.disabled) {
810             return;
811         }
812         
813         
814         Roo.log('button on click ');
815         if(this.preventDefault){
816             e.preventDefault();
817         }
818         if (this.pressed === true || this.pressed === false) {
819             this.pressed = !this.pressed;
820             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
821             this.fireEvent('toggle', this, e, this.pressed);
822         }
823         
824         
825         this.fireEvent('click', this, e);
826     },
827     
828     /**
829      * Enables this button
830      */
831     enable : function()
832     {
833         this.disabled = false;
834         this.el.removeClass('disabled');
835     },
836     
837     /**
838      * Disable this button
839      */
840     disable : function()
841     {
842         this.disabled = true;
843         this.el.addClass('disabled');
844     },
845      /**
846      * sets the active state on/off, 
847      * @param {Boolean} state (optional) Force a particular state
848      */
849     setActive : function(v) {
850         
851         this.el[v ? 'addClass' : 'removeClass']('active');
852     },
853      /**
854      * toggles the current active state 
855      */
856     toggleActive : function()
857     {
858        var active = this.el.hasClass('active');
859        this.setActive(!active);
860        
861         
862     },
863     setText : function(str)
864     {
865         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
866     },
867     getText : function()
868     {
869         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870     },
871     hide: function() {
872        
873      
874         this.el.hide();   
875     },
876     show: function() {
877        
878         this.el.show();   
879     },
880     setWeight : function(str)
881     {
882           this.el.removeClass(this.weightClass);
883         this.el.addClass('btn-' + str);        
884     }
885     
886     
887 });
888
889  /*
890  * - LGPL
891  *
892  * column
893  * 
894  */
895
896 /**
897  * @class Roo.bootstrap.Column
898  * @extends Roo.bootstrap.Component
899  * Bootstrap Column class
900  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
901  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
902  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
903  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
904  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
905  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
906  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
907  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
908  *
909  * 
910  * @cfg {Boolean} hidden (true|false) hide the element
911  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
912  * @cfg {String} fa (ban|check|...) font awesome icon
913  * @cfg {Number} fasize (1|2|....) font awsome size
914
915  * @cfg {String} icon (info-sign|check|...) glyphicon name
916
917  * @cfg {String} html content of column.
918  * 
919  * @constructor
920  * Create a new Column
921  * @param {Object} config The config object
922  */
923
924 Roo.bootstrap.Column = function(config){
925     Roo.bootstrap.Column.superclass.constructor.call(this, config);
926 };
927
928 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
929     
930     xs: false,
931     sm: false,
932     md: false,
933     lg: false,
934     xsoff: false,
935     smoff: false,
936     mdoff: false,
937     lgoff: false,
938     html: '',
939     offset: 0,
940     alert: false,
941     fa: false,
942     icon : false,
943     hidden : false,
944     fasize : 1,
945     
946     getAutoCreate : function(){
947         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
948         
949         cfg = {
950             tag: 'div',
951             cls: 'column'
952         };
953         
954         var settings=this;
955         ['xs','sm','md','lg'].map(function(size){
956             //Roo.log( size + ':' + settings[size]);
957             
958             if (settings[size+'off'] !== false) {
959                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
960             }
961             
962             if (settings[size] === false) {
963                 return;
964             }
965             
966             if (!settings[size]) { // 0 = hidden
967                 cfg.cls += ' hidden-' + size;
968                 return;
969             }
970             cfg.cls += ' col-' + size + '-' + settings[size];
971             
972         });
973         
974         if (this.hidden) {
975             cfg.cls += ' hidden';
976         }
977         
978         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
979             cfg.cls +=' alert alert-' + this.alert;
980         }
981         
982         
983         if (this.html.length) {
984             cfg.html = this.html;
985         }
986         if (this.fa) {
987             var fasize = '';
988             if (this.fasize > 1) {
989                 fasize = ' fa-' + this.fasize + 'x';
990             }
991             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
992             
993             
994         }
995         if (this.icon) {
996             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
997         }
998         
999         return cfg;
1000     }
1001    
1002 });
1003
1004  
1005
1006  /*
1007  * - LGPL
1008  *
1009  * page container.
1010  * 
1011  */
1012
1013
1014 /**
1015  * @class Roo.bootstrap.Container
1016  * @extends Roo.bootstrap.Component
1017  * Bootstrap Container class
1018  * @cfg {Boolean} jumbotron is it a jumbotron element
1019  * @cfg {String} html content of element
1020  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1021  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1022  * @cfg {String} header content of header (for panel)
1023  * @cfg {String} footer content of footer (for panel)
1024  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1025  * @cfg {String} tag (header|aside|section) type of HTML tag.
1026  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1027  * @cfg {String} fa font awesome icon
1028  * @cfg {String} icon (info-sign|check|...) glyphicon name
1029  * @cfg {Boolean} hidden (true|false) hide the element
1030  * @cfg {Boolean} expandable (true|false) default false
1031  * @cfg {Boolean} expanded (true|false) default true
1032  * @cfg {String} rheader contet on the right of header
1033  * @cfg {Boolean} clickable (true|false) default false
1034
1035  *     
1036  * @constructor
1037  * Create a new Container
1038  * @param {Object} config The config object
1039  */
1040
1041 Roo.bootstrap.Container = function(config){
1042     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1043     
1044     this.addEvents({
1045         // raw events
1046          /**
1047          * @event expand
1048          * After the panel has been expand
1049          * 
1050          * @param {Roo.bootstrap.Container} this
1051          */
1052         "expand" : true,
1053         /**
1054          * @event collapse
1055          * After the panel has been collapsed
1056          * 
1057          * @param {Roo.bootstrap.Container} this
1058          */
1059         "collapse" : true,
1060         /**
1061          * @event click
1062          * When a element is chick
1063          * @param {Roo.bootstrap.Container} this
1064          * @param {Roo.EventObject} e
1065          */
1066         "click" : true
1067     });
1068 };
1069
1070 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1071     
1072     jumbotron : false,
1073     well: '',
1074     panel : '',
1075     header: '',
1076     footer : '',
1077     sticky: '',
1078     tag : false,
1079     alert : false,
1080     fa: false,
1081     icon : false,
1082     expandable : false,
1083     rheader : '',
1084     expanded : true,
1085     clickable: false,
1086   
1087      
1088     getChildContainer : function() {
1089         
1090         if(!this.el){
1091             return false;
1092         }
1093         
1094         if (this.panel.length) {
1095             return this.el.select('.panel-body',true).first();
1096         }
1097         
1098         return this.el;
1099     },
1100     
1101     
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag : this.tag || 'div',
1106             html : '',
1107             cls : ''
1108         };
1109         if (this.jumbotron) {
1110             cfg.cls = 'jumbotron';
1111         }
1112         
1113         
1114         
1115         // - this is applied by the parent..
1116         //if (this.cls) {
1117         //    cfg.cls = this.cls + '';
1118         //}
1119         
1120         if (this.sticky.length) {
1121             
1122             var bd = Roo.get(document.body);
1123             if (!bd.hasClass('bootstrap-sticky')) {
1124                 bd.addClass('bootstrap-sticky');
1125                 Roo.select('html',true).setStyle('height', '100%');
1126             }
1127              
1128             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1129         }
1130         
1131         
1132         if (this.well.length) {
1133             switch (this.well) {
1134                 case 'lg':
1135                 case 'sm':
1136                     cfg.cls +=' well well-' +this.well;
1137                     break;
1138                 default:
1139                     cfg.cls +=' well';
1140                     break;
1141             }
1142         }
1143         
1144         if (this.hidden) {
1145             cfg.cls += ' hidden';
1146         }
1147         
1148         
1149         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1150             cfg.cls +=' alert alert-' + this.alert;
1151         }
1152         
1153         var body = cfg;
1154         
1155         if (this.panel.length) {
1156             cfg.cls += ' panel panel-' + this.panel;
1157             cfg.cn = [];
1158             if (this.header.length) {
1159                 
1160                 var h = [];
1161                 
1162                 if(this.expandable){
1163                     
1164                     cfg.cls = cfg.cls + ' expandable';
1165                     
1166                     h.push({
1167                         tag: 'i',
1168                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1169                     });
1170                     
1171                 }
1172                 
1173                 h.push(
1174                     {
1175                         tag: 'span',
1176                         cls : 'panel-title',
1177                         html : (this.expandable ? '&nbsp;' : '') + this.header
1178                     },
1179                     {
1180                         tag: 'span',
1181                         cls: 'panel-header-right',
1182                         html: this.rheader
1183                     }
1184                 );
1185                 
1186                 cfg.cn.push({
1187                     cls : 'panel-heading',
1188                     style : this.expandable ? 'cursor: pointer' : '',
1189                     cn : h
1190                 });
1191                 
1192             }
1193             
1194             body = false;
1195             cfg.cn.push({
1196                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1197                 html : this.html
1198             });
1199             
1200             
1201             if (this.footer.length) {
1202                 cfg.cn.push({
1203                     cls : 'panel-footer',
1204                     html : this.footer
1205                     
1206                 });
1207             }
1208             
1209         }
1210         
1211         if (body) {
1212             body.html = this.html || cfg.html;
1213             // prefix with the icons..
1214             if (this.fa) {
1215                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1216             }
1217             if (this.icon) {
1218                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1219             }
1220             
1221             
1222         }
1223         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1224             cfg.cls =  'container';
1225         }
1226         
1227         return cfg;
1228     },
1229     
1230     initEvents: function() 
1231     {
1232         if(this.expandable){
1233             var headerEl = this.headerEl();
1234         
1235             if(headerEl){
1236                 headerEl.on('click', this.onToggleClick, this);
1237             }
1238         }
1239         
1240         if(this.clickable){
1241             this.el.on('click', this.onClick, this);
1242         }
1243         
1244     },
1245     
1246     onToggleClick : function()
1247     {
1248         var headerEl = this.headerEl();
1249         
1250         if(!headerEl){
1251             return;
1252         }
1253         
1254         if(this.expanded){
1255             this.collapse();
1256             return;
1257         }
1258         
1259         this.expand();
1260     },
1261     
1262     expand : function()
1263     {
1264         if(this.fireEvent('expand', this)) {
1265             
1266             this.expanded = true;
1267             
1268             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1269             
1270             this.el.select('.panel-body',true).first().removeClass('hide');
1271             
1272             var toggleEl = this.toggleEl();
1273
1274             if(!toggleEl){
1275                 return;
1276             }
1277
1278             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1279         }
1280         
1281     },
1282     
1283     collapse : function()
1284     {
1285         if(this.fireEvent('collapse', this)) {
1286             
1287             this.expanded = false;
1288             
1289             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1290             this.el.select('.panel-body',true).first().addClass('hide');
1291         
1292             var toggleEl = this.toggleEl();
1293
1294             if(!toggleEl){
1295                 return;
1296             }
1297
1298             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1299         }
1300     },
1301     
1302     toggleEl : function()
1303     {
1304         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1305             return;
1306         }
1307         
1308         return this.el.select('.panel-heading .fa',true).first();
1309     },
1310     
1311     headerEl : function()
1312     {
1313         if(!this.el || !this.panel.length || !this.header.length){
1314             return;
1315         }
1316         
1317         return this.el.select('.panel-heading',true).first()
1318     },
1319     
1320     bodyEl : function()
1321     {
1322         if(!this.el || !this.panel.length){
1323             return;
1324         }
1325         
1326         return this.el.select('.panel-body',true).first()
1327     },
1328     
1329     titleEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-title',true).first();
1336     },
1337     
1338     setTitle : function(v)
1339     {
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return;
1344         }
1345         
1346         titleEl.dom.innerHTML = v;
1347     },
1348     
1349     getTitle : function()
1350     {
1351         
1352         var titleEl = this.titleEl();
1353         
1354         if(!titleEl){
1355             return '';
1356         }
1357         
1358         return titleEl.dom.innerHTML;
1359     },
1360     
1361     setRightTitle : function(v)
1362     {
1363         var t = this.el.select('.panel-header-right',true).first();
1364         
1365         if(!t){
1366             return;
1367         }
1368         
1369         t.dom.innerHTML = v;
1370     },
1371     
1372     onClick : function(e)
1373     {
1374         e.preventDefault();
1375         
1376         this.fireEvent('click', this, e);
1377     }
1378    
1379 });
1380
1381  /*
1382  * - LGPL
1383  *
1384  * image
1385  * 
1386  */
1387
1388
1389 /**
1390  * @class Roo.bootstrap.Img
1391  * @extends Roo.bootstrap.Component
1392  * Bootstrap Img class
1393  * @cfg {Boolean} imgResponsive false | true
1394  * @cfg {String} border rounded | circle | thumbnail
1395  * @cfg {String} src image source
1396  * @cfg {String} alt image alternative text
1397  * @cfg {String} href a tag href
1398  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1399  * @cfg {String} xsUrl xs image source
1400  * @cfg {String} smUrl sm image source
1401  * @cfg {String} mdUrl md image source
1402  * @cfg {String} lgUrl lg image source
1403  * 
1404  * @constructor
1405  * Create a new Input
1406  * @param {Object} config The config object
1407  */
1408
1409 Roo.bootstrap.Img = function(config){
1410     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1411     
1412     this.addEvents({
1413         // img events
1414         /**
1415          * @event click
1416          * The img click event for the img.
1417          * @param {Roo.EventObject} e
1418          */
1419         "click" : true
1420     });
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1424     
1425     imgResponsive: true,
1426     border: '',
1427     src: 'about:blank',
1428     href: false,
1429     target: false,
1430     xsUrl: '',
1431     smUrl: '',
1432     mdUrl: '',
1433     lgUrl: '',
1434
1435     getAutoCreate : function()
1436     {   
1437         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1438             return this.createSingleImg();
1439         }
1440         
1441         var cfg = {
1442             tag: 'div',
1443             cls: 'roo-image-responsive-group',
1444             cn: []
1445         };
1446         var _this = this;
1447         
1448         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1449             
1450             if(!_this[size + 'Url']){
1451                 return;
1452             }
1453             
1454             var img = {
1455                 tag: 'img',
1456                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1457                 html: _this.html || cfg.html,
1458                 src: _this[size + 'Url']
1459             };
1460             
1461             img.cls += ' roo-image-responsive-' + size;
1462             
1463             var s = ['xs', 'sm', 'md', 'lg'];
1464             
1465             s.splice(s.indexOf(size), 1);
1466             
1467             Roo.each(s, function(ss){
1468                 img.cls += ' hidden-' + ss;
1469             });
1470             
1471             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1472                 cfg.cls += ' img-' + _this.border;
1473             }
1474             
1475             if(_this.alt){
1476                 cfg.alt = _this.alt;
1477             }
1478             
1479             if(_this.href){
1480                 var a = {
1481                     tag: 'a',
1482                     href: _this.href,
1483                     cn: [
1484                         img
1485                     ]
1486                 };
1487
1488                 if(this.target){
1489                     a.target = _this.target;
1490                 }
1491             }
1492             
1493             cfg.cn.push((_this.href) ? a : img);
1494             
1495         });
1496         
1497         return cfg;
1498     },
1499     
1500     createSingleImg : function()
1501     {
1502         var cfg = {
1503             tag: 'img',
1504             cls: (this.imgResponsive) ? 'img-responsive' : '',
1505             html : null,
1506             src : 'about:blank'  // just incase src get's set to undefined?!?
1507         };
1508         
1509         cfg.html = this.html || cfg.html;
1510         
1511         cfg.src = this.src || cfg.src;
1512         
1513         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1514             cfg.cls += ' img-' + this.border;
1515         }
1516         
1517         if(this.alt){
1518             cfg.alt = this.alt;
1519         }
1520         
1521         if(this.href){
1522             var a = {
1523                 tag: 'a',
1524                 href: this.href,
1525                 cn: [
1526                     cfg
1527                 ]
1528             };
1529             
1530             if(this.target){
1531                 a.target = this.target;
1532             }
1533             
1534         }
1535         
1536         return (this.href) ? a : cfg;
1537     },
1538     
1539     initEvents: function() 
1540     {
1541         if(!this.href){
1542             this.el.on('click', this.onClick, this);
1543         }
1544         
1545     },
1546     
1547     onClick : function(e)
1548     {
1549         Roo.log('img onclick');
1550         this.fireEvent('click', this, e);
1551     },
1552     /**
1553      * Sets the url of the image - used to update it
1554      * @param {String} url the url of the image
1555      */
1556     
1557     setSrc : function(url)
1558     {
1559         this.src =  url;
1560         
1561         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1562             this.el.dom.src =  url;
1563             return;
1564         }
1565         
1566         this.el.select('img', true).first().dom.src =  url;
1567     }
1568     
1569     
1570    
1571 });
1572
1573  /*
1574  * - LGPL
1575  *
1576  * image
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Link
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Link Class
1585  * @cfg {String} alt image alternative text
1586  * @cfg {String} href a tag href
1587  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1588  * @cfg {String} html the content of the link.
1589  * @cfg {String} anchor name for the anchor link
1590  * @cfg {String} fa - favicon
1591
1592  * @cfg {Boolean} preventDefault (true | false) default false
1593
1594  * 
1595  * @constructor
1596  * Create a new Input
1597  * @param {Object} config The config object
1598  */
1599
1600 Roo.bootstrap.Link = function(config){
1601     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1602     
1603     this.addEvents({
1604         // img events
1605         /**
1606          * @event click
1607          * The img click event for the img.
1608          * @param {Roo.EventObject} e
1609          */
1610         "click" : true
1611     });
1612 };
1613
1614 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1615     
1616     href: false,
1617     target: false,
1618     preventDefault: false,
1619     anchor : false,
1620     alt : false,
1621     fa: false,
1622
1623
1624     getAutoCreate : function()
1625     {
1626         var html = this.html || '';
1627         
1628         if (this.fa !== false) {
1629             html = '<i class="fa fa-' + this.fa + '"></i>';
1630         }
1631         var cfg = {
1632             tag: 'a'
1633         };
1634         // anchor's do not require html/href...
1635         if (this.anchor === false) {
1636             cfg.html = html;
1637             cfg.href = this.href || '#';
1638         } else {
1639             cfg.name = this.anchor;
1640             if (this.html !== false || this.fa !== false) {
1641                 cfg.html = html;
1642             }
1643             if (this.href !== false) {
1644                 cfg.href = this.href;
1645             }
1646         }
1647         
1648         if(this.alt !== false){
1649             cfg.alt = this.alt;
1650         }
1651         
1652         
1653         if(this.target !== false) {
1654             cfg.target = this.target;
1655         }
1656         
1657         return cfg;
1658     },
1659     
1660     initEvents: function() {
1661         
1662         if(!this.href || this.preventDefault){
1663             this.el.on('click', this.onClick, this);
1664         }
1665     },
1666     
1667     onClick : function(e)
1668     {
1669         if(this.preventDefault){
1670             e.preventDefault();
1671         }
1672         //Roo.log('img onclick');
1673         this.fireEvent('click', this, e);
1674     }
1675    
1676 });
1677
1678  /*
1679  * - LGPL
1680  *
1681  * header
1682  * 
1683  */
1684
1685 /**
1686  * @class Roo.bootstrap.Header
1687  * @extends Roo.bootstrap.Component
1688  * Bootstrap Header class
1689  * @cfg {String} html content of header
1690  * @cfg {Number} level (1|2|3|4|5|6) default 1
1691  * 
1692  * @constructor
1693  * Create a new Header
1694  * @param {Object} config The config object
1695  */
1696
1697
1698 Roo.bootstrap.Header  = function(config){
1699     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1700 };
1701
1702 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1703     
1704     //href : false,
1705     html : false,
1706     level : 1,
1707     
1708     
1709     
1710     getAutoCreate : function(){
1711         
1712         
1713         
1714         var cfg = {
1715             tag: 'h' + (1 *this.level),
1716             html: this.html || ''
1717         } ;
1718         
1719         return cfg;
1720     }
1721    
1722 });
1723
1724  
1725
1726  /*
1727  * Based on:
1728  * Ext JS Library 1.1.1
1729  * Copyright(c) 2006-2007, Ext JS, LLC.
1730  *
1731  * Originally Released Under LGPL - original licence link has changed is not relivant.
1732  *
1733  * Fork - LGPL
1734  * <script type="text/javascript">
1735  */
1736  
1737 /**
1738  * @class Roo.bootstrap.MenuMgr
1739  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1740  * @singleton
1741  */
1742 Roo.bootstrap.MenuMgr = function(){
1743    var menus, active, groups = {}, attached = false, lastShow = new Date();
1744
1745    // private - called when first menu is created
1746    function init(){
1747        menus = {};
1748        active = new Roo.util.MixedCollection();
1749        Roo.get(document).addKeyListener(27, function(){
1750            if(active.length > 0){
1751                hideAll();
1752            }
1753        });
1754    }
1755
1756    // private
1757    function hideAll(){
1758        if(active && active.length > 0){
1759            var c = active.clone();
1760            c.each(function(m){
1761                m.hide();
1762            });
1763        }
1764    }
1765
1766    // private
1767    function onHide(m){
1768        active.remove(m);
1769        if(active.length < 1){
1770            Roo.get(document).un("mouseup", onMouseDown);
1771             
1772            attached = false;
1773        }
1774    }
1775
1776    // private
1777    function onShow(m){
1778        var last = active.last();
1779        lastShow = new Date();
1780        active.add(m);
1781        if(!attached){
1782           Roo.get(document).on("mouseup", onMouseDown);
1783            
1784            attached = true;
1785        }
1786        if(m.parentMenu){
1787           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1788           m.parentMenu.activeChild = m;
1789        }else if(last && last.isVisible()){
1790           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1791        }
1792    }
1793
1794    // private
1795    function onBeforeHide(m){
1796        if(m.activeChild){
1797            m.activeChild.hide();
1798        }
1799        if(m.autoHideTimer){
1800            clearTimeout(m.autoHideTimer);
1801            delete m.autoHideTimer;
1802        }
1803    }
1804
1805    // private
1806    function onBeforeShow(m){
1807        var pm = m.parentMenu;
1808        if(!pm && !m.allowOtherMenus){
1809            hideAll();
1810        }else if(pm && pm.activeChild && active != m){
1811            pm.activeChild.hide();
1812        }
1813    }
1814
1815    // private this should really trigger on mouseup..
1816    function onMouseDown(e){
1817         Roo.log("on Mouse Up");
1818         
1819         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1820             Roo.log("MenuManager hideAll");
1821             hideAll();
1822             e.stopEvent();
1823         }
1824         
1825         
1826    }
1827
1828    // private
1829    function onBeforeCheck(mi, state){
1830        if(state){
1831            var g = groups[mi.group];
1832            for(var i = 0, l = g.length; i < l; i++){
1833                if(g[i] != mi){
1834                    g[i].setChecked(false);
1835                }
1836            }
1837        }
1838    }
1839
1840    return {
1841
1842        /**
1843         * Hides all menus that are currently visible
1844         */
1845        hideAll : function(){
1846             hideAll();  
1847        },
1848
1849        // private
1850        register : function(menu){
1851            if(!menus){
1852                init();
1853            }
1854            menus[menu.id] = menu;
1855            menu.on("beforehide", onBeforeHide);
1856            menu.on("hide", onHide);
1857            menu.on("beforeshow", onBeforeShow);
1858            menu.on("show", onShow);
1859            var g = menu.group;
1860            if(g && menu.events["checkchange"]){
1861                if(!groups[g]){
1862                    groups[g] = [];
1863                }
1864                groups[g].push(menu);
1865                menu.on("checkchange", onCheck);
1866            }
1867        },
1868
1869         /**
1870          * Returns a {@link Roo.menu.Menu} object
1871          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1872          * be used to generate and return a new Menu instance.
1873          */
1874        get : function(menu){
1875            if(typeof menu == "string"){ // menu id
1876                return menus[menu];
1877            }else if(menu.events){  // menu instance
1878                return menu;
1879            }
1880            /*else if(typeof menu.length == 'number'){ // array of menu items?
1881                return new Roo.bootstrap.Menu({items:menu});
1882            }else{ // otherwise, must be a config
1883                return new Roo.bootstrap.Menu(menu);
1884            }
1885            */
1886            return false;
1887        },
1888
1889        // private
1890        unregister : function(menu){
1891            delete menus[menu.id];
1892            menu.un("beforehide", onBeforeHide);
1893            menu.un("hide", onHide);
1894            menu.un("beforeshow", onBeforeShow);
1895            menu.un("show", onShow);
1896            var g = menu.group;
1897            if(g && menu.events["checkchange"]){
1898                groups[g].remove(menu);
1899                menu.un("checkchange", onCheck);
1900            }
1901        },
1902
1903        // private
1904        registerCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                if(!groups[g]){
1908                    groups[g] = [];
1909                }
1910                groups[g].push(menuItem);
1911                menuItem.on("beforecheckchange", onBeforeCheck);
1912            }
1913        },
1914
1915        // private
1916        unregisterCheckable : function(menuItem){
1917            var g = menuItem.group;
1918            if(g){
1919                groups[g].remove(menuItem);
1920                menuItem.un("beforecheckchange", onBeforeCheck);
1921            }
1922        }
1923    };
1924 }();/*
1925  * - LGPL
1926  *
1927  * menu
1928  * 
1929  */
1930
1931 /**
1932  * @class Roo.bootstrap.Menu
1933  * @extends Roo.bootstrap.Component
1934  * Bootstrap Menu class - container for MenuItems
1935  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1936  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1937  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1938  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1939  * 
1940  * @constructor
1941  * Create a new Menu
1942  * @param {Object} config The config object
1943  */
1944
1945
1946 Roo.bootstrap.Menu = function(config){
1947     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1948     if (this.registerMenu && this.type != 'treeview')  {
1949         Roo.bootstrap.MenuMgr.register(this);
1950     }
1951     this.addEvents({
1952         /**
1953          * @event beforeshow
1954          * Fires before this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         beforeshow : true,
1958         /**
1959          * @event beforehide
1960          * Fires before this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         beforehide : true,
1964         /**
1965          * @event show
1966          * Fires after this menu is displayed
1967          * @param {Roo.menu.Menu} this
1968          */
1969         show : true,
1970         /**
1971          * @event hide
1972          * Fires after this menu is hidden
1973          * @param {Roo.menu.Menu} this
1974          */
1975         hide : true,
1976         /**
1977          * @event click
1978          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1979          * @param {Roo.menu.Menu} this
1980          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1981          * @param {Roo.EventObject} e
1982          */
1983         click : true,
1984         /**
1985          * @event mouseover
1986          * Fires when the mouse is hovering over this menu
1987          * @param {Roo.menu.Menu} this
1988          * @param {Roo.EventObject} e
1989          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1990          */
1991         mouseover : true,
1992         /**
1993          * @event mouseout
1994          * Fires when the mouse exits this menu
1995          * @param {Roo.menu.Menu} this
1996          * @param {Roo.EventObject} e
1997          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1998          */
1999         mouseout : true,
2000         /**
2001          * @event itemclick
2002          * Fires when a menu item contained in this menu is clicked
2003          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2004          * @param {Roo.EventObject} e
2005          */
2006         itemclick: true
2007     });
2008     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2009 };
2010
2011 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2012     
2013    /// html : false,
2014     //align : '',
2015     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2016     type: false,
2017     /**
2018      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2019      */
2020     registerMenu : true,
2021     
2022     menuItems :false, // stores the menu items..
2023     
2024     hidden:true,
2025         
2026     parentMenu : false,
2027     
2028     stopEvent : true,
2029     
2030     isLink : false,
2031     
2032     getChildContainer : function() {
2033         return this.el;  
2034     },
2035     
2036     getAutoCreate : function(){
2037          
2038         //if (['right'].indexOf(this.align)!==-1) {
2039         //    cfg.cn[1].cls += ' pull-right'
2040         //}
2041         
2042         
2043         var cfg = {
2044             tag : 'ul',
2045             cls : 'dropdown-menu' ,
2046             style : 'z-index:1000'
2047             
2048         };
2049         
2050         if (this.type === 'submenu') {
2051             cfg.cls = 'submenu active';
2052         }
2053         if (this.type === 'treeview') {
2054             cfg.cls = 'treeview-menu';
2055         }
2056         
2057         return cfg;
2058     },
2059     initEvents : function() {
2060         
2061        // Roo.log("ADD event");
2062        // Roo.log(this.triggerEl.dom);
2063         
2064         this.triggerEl.on('click', this.onTriggerClick, this);
2065         
2066         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2067         
2068         this.triggerEl.addClass('dropdown-toggle');
2069         
2070         if (Roo.isTouch) {
2071             this.el.on('touchstart'  , this.onTouch, this);
2072         }
2073         this.el.on('click' , this.onClick, this);
2074
2075         this.el.on("mouseover", this.onMouseOver, this);
2076         this.el.on("mouseout", this.onMouseOut, this);
2077         
2078     },
2079     
2080     findTargetItem : function(e)
2081     {
2082         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2083         if(!t){
2084             return false;
2085         }
2086         //Roo.log(t);         Roo.log(t.id);
2087         if(t && t.id){
2088             //Roo.log(this.menuitems);
2089             return this.menuitems.get(t.id);
2090             
2091             //return this.items.get(t.menuItemId);
2092         }
2093         
2094         return false;
2095     },
2096     
2097     onTouch : function(e) 
2098     {
2099         Roo.log("menu.onTouch");
2100         //e.stopEvent(); this make the user popdown broken
2101         this.onClick(e);
2102     },
2103     
2104     onClick : function(e)
2105     {
2106         Roo.log("menu.onClick");
2107         
2108         var t = this.findTargetItem(e);
2109         if(!t || t.isContainer){
2110             return;
2111         }
2112         Roo.log(e);
2113         /*
2114         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2115             if(t == this.activeItem && t.shouldDeactivate(e)){
2116                 this.activeItem.deactivate();
2117                 delete this.activeItem;
2118                 return;
2119             }
2120             if(t.canActivate){
2121                 this.setActiveItem(t, true);
2122             }
2123             return;
2124             
2125             
2126         }
2127         */
2128        
2129         Roo.log('pass click event');
2130         
2131         t.onClick(e);
2132         
2133         this.fireEvent("click", this, t, e);
2134         
2135         var _this = this;
2136         
2137         if(!t.href.length || t.href == '#'){
2138             (function() { _this.hide(); }).defer(100);
2139         }
2140         
2141     },
2142     
2143     onMouseOver : function(e){
2144         var t  = this.findTargetItem(e);
2145         //Roo.log(t);
2146         //if(t){
2147         //    if(t.canActivate && !t.disabled){
2148         //        this.setActiveItem(t, true);
2149         //    }
2150         //}
2151         
2152         this.fireEvent("mouseover", this, e, t);
2153     },
2154     isVisible : function(){
2155         return !this.hidden;
2156     },
2157      onMouseOut : function(e){
2158         var t  = this.findTargetItem(e);
2159         
2160         //if(t ){
2161         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2162         //        this.activeItem.deactivate();
2163         //        delete this.activeItem;
2164         //    }
2165         //}
2166         this.fireEvent("mouseout", this, e, t);
2167     },
2168     
2169     
2170     /**
2171      * Displays this menu relative to another element
2172      * @param {String/HTMLElement/Roo.Element} element The element to align to
2173      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2174      * the element (defaults to this.defaultAlign)
2175      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2176      */
2177     show : function(el, pos, parentMenu){
2178         this.parentMenu = parentMenu;
2179         if(!this.el){
2180             this.render();
2181         }
2182         this.fireEvent("beforeshow", this);
2183         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2184     },
2185      /**
2186      * Displays this menu at a specific xy position
2187      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2188      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2189      */
2190     showAt : function(xy, parentMenu, /* private: */_e){
2191         this.parentMenu = parentMenu;
2192         if(!this.el){
2193             this.render();
2194         }
2195         if(_e !== false){
2196             this.fireEvent("beforeshow", this);
2197             //xy = this.el.adjustForConstraints(xy);
2198         }
2199         
2200         //this.el.show();
2201         this.hideMenuItems();
2202         this.hidden = false;
2203         this.triggerEl.addClass('open');
2204         
2205         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2206             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2207         }
2208         
2209         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2210             this.el.setXY(xy);
2211         }
2212         
2213         this.focus();
2214         this.fireEvent("show", this);
2215     },
2216     
2217     focus : function(){
2218         return;
2219         if(!this.hidden){
2220             this.doFocus.defer(50, this);
2221         }
2222     },
2223
2224     doFocus : function(){
2225         if(!this.hidden){
2226             this.focusEl.focus();
2227         }
2228     },
2229
2230     /**
2231      * Hides this menu and optionally all parent menus
2232      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2233      */
2234     hide : function(deep)
2235     {
2236         
2237         this.hideMenuItems();
2238         if(this.el && this.isVisible()){
2239             this.fireEvent("beforehide", this);
2240             if(this.activeItem){
2241                 this.activeItem.deactivate();
2242                 this.activeItem = null;
2243             }
2244             this.triggerEl.removeClass('open');;
2245             this.hidden = true;
2246             this.fireEvent("hide", this);
2247         }
2248         if(deep === true && this.parentMenu){
2249             this.parentMenu.hide(true);
2250         }
2251     },
2252     
2253     onTriggerClick : function(e)
2254     {
2255         Roo.log('trigger click');
2256         
2257         var target = e.getTarget();
2258         
2259         Roo.log(target.nodeName.toLowerCase());
2260         
2261         if(target.nodeName.toLowerCase() === 'i'){
2262             e.preventDefault();
2263         }
2264         
2265     },
2266     
2267     onTriggerPress  : function(e)
2268     {
2269         Roo.log('trigger press');
2270         //Roo.log(e.getTarget());
2271        // Roo.log(this.triggerEl.dom);
2272        
2273         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2274         var pel = Roo.get(e.getTarget());
2275         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2276             Roo.log('is treeview or dropdown?');
2277             return;
2278         }
2279         
2280         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2281             return;
2282         }
2283         
2284         if (this.isVisible()) {
2285             Roo.log('hide');
2286             this.hide();
2287         } else {
2288             Roo.log('show');
2289             this.show(this.triggerEl, false, false);
2290         }
2291         
2292         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2293             e.stopEvent();
2294         }
2295         
2296     },
2297        
2298     
2299     hideMenuItems : function()
2300     {
2301         Roo.log("hide Menu Items");
2302         if (!this.el) { 
2303             return;
2304         }
2305         //$(backdrop).remove()
2306         this.el.select('.open',true).each(function(aa) {
2307             
2308             aa.removeClass('open');
2309           //var parent = getParent($(this))
2310           //var relatedTarget = { relatedTarget: this }
2311           
2312            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2313           //if (e.isDefaultPrevented()) return
2314            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2315         });
2316     },
2317     addxtypeChild : function (tree, cntr) {
2318         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2319           
2320         this.menuitems.add(comp);
2321         return comp;
2322
2323     },
2324     getEl : function()
2325     {
2326         Roo.log(this.el);
2327         return this.el;
2328     }
2329 });
2330
2331  
2332  /*
2333  * - LGPL
2334  *
2335  * menu item
2336  * 
2337  */
2338
2339
2340 /**
2341  * @class Roo.bootstrap.MenuItem
2342  * @extends Roo.bootstrap.Component
2343  * Bootstrap MenuItem class
2344  * @cfg {String} html the menu label
2345  * @cfg {String} href the link
2346  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2347  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2348  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2349  * @cfg {String} fa favicon to show on left of menu item.
2350  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2351  * 
2352  * 
2353  * @constructor
2354  * Create a new MenuItem
2355  * @param {Object} config The config object
2356  */
2357
2358
2359 Roo.bootstrap.MenuItem = function(config){
2360     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2361     this.addEvents({
2362         // raw events
2363         /**
2364          * @event click
2365          * The raw click event for the entire grid.
2366          * @param {Roo.bootstrap.MenuItem} this
2367          * @param {Roo.EventObject} e
2368          */
2369         "click" : true
2370     });
2371 };
2372
2373 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2374     
2375     href : false,
2376     html : false,
2377     preventDefault: false,
2378     isContainer : false,
2379     active : false,
2380     fa: false,
2381     
2382     getAutoCreate : function(){
2383         
2384         if(this.isContainer){
2385             return {
2386                 tag: 'li',
2387                 cls: 'dropdown-menu-item'
2388             };
2389         }
2390         var ctag = {
2391             tag: 'span',
2392             html: 'Link'
2393         };
2394         
2395         var anc = {
2396             tag : 'a',
2397             href : '#',
2398             cn : [  ]
2399         };
2400         
2401         if (this.fa !== false) {
2402             anc.cn.push({
2403                 tag : 'i',
2404                 cls : 'fa fa-' + this.fa
2405             });
2406         }
2407         
2408         anc.cn.push(ctag);
2409         
2410         
2411         var cfg= {
2412             tag: 'li',
2413             cls: 'dropdown-menu-item',
2414             cn: [ anc ]
2415         };
2416         if (this.parent().type == 'treeview') {
2417             cfg.cls = 'treeview-menu';
2418         }
2419         if (this.active) {
2420             cfg.cls += ' active';
2421         }
2422         
2423         
2424         
2425         anc.href = this.href || cfg.cn[0].href ;
2426         ctag.html = this.html || cfg.cn[0].html ;
2427         return cfg;
2428     },
2429     
2430     initEvents: function()
2431     {
2432         if (this.parent().type == 'treeview') {
2433             this.el.select('a').on('click', this.onClick, this);
2434         }
2435         
2436         if (this.menu) {
2437             this.menu.parentType = this.xtype;
2438             this.menu.triggerEl = this.el;
2439             this.menu = this.addxtype(Roo.apply({}, this.menu));
2440         }
2441         
2442     },
2443     onClick : function(e)
2444     {
2445         Roo.log('item on click ');
2446         
2447         if(this.preventDefault){
2448             e.preventDefault();
2449         }
2450         //this.parent().hideMenuItems();
2451         
2452         this.fireEvent('click', this, e);
2453     },
2454     getEl : function()
2455     {
2456         return this.el;
2457     } 
2458 });
2459
2460  
2461
2462  /*
2463  * - LGPL
2464  *
2465  * menu separator
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuSeparator
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuSeparator class
2474  * 
2475  * @constructor
2476  * Create a new MenuItem
2477  * @param {Object} config The config object
2478  */
2479
2480
2481 Roo.bootstrap.MenuSeparator = function(config){
2482     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2483 };
2484
2485 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2486     
2487     getAutoCreate : function(){
2488         var cfg = {
2489             cls: 'divider',
2490             tag : 'li'
2491         };
2492         
2493         return cfg;
2494     }
2495    
2496 });
2497
2498  
2499
2500  
2501 /*
2502 * Licence: LGPL
2503 */
2504
2505 /**
2506  * @class Roo.bootstrap.Modal
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap Modal class
2509  * @cfg {String} title Title of dialog
2510  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2511  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2512  * @cfg {Boolean} specificTitle default false
2513  * @cfg {Array} buttons Array of buttons or standard button set..
2514  * @cfg {String} buttonPosition (left|right|center) default right
2515  * @cfg {Boolean} animate default true
2516  * @cfg {Boolean} allow_close default true
2517  * @cfg {Boolean} fitwindow default false
2518  * @cfg {String} size (sm|lg) default empty
2519  *
2520  *
2521  * @constructor
2522  * Create a new Modal Dialog
2523  * @param {Object} config The config object
2524  */
2525
2526 Roo.bootstrap.Modal = function(config){
2527     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2528     this.addEvents({
2529         // raw events
2530         /**
2531          * @event btnclick
2532          * The raw btnclick event for the button
2533          * @param {Roo.EventObject} e
2534          */
2535         "btnclick" : true,
2536         /**
2537          * @event resize
2538          * Fire when dialog resize
2539          * @param {Roo.bootstrap.Modal} this
2540          * @param {Roo.EventObject} e
2541          */
2542         "resize" : true
2543     });
2544     this.buttons = this.buttons || [];
2545
2546     if (this.tmpl) {
2547         this.tmpl = Roo.factory(this.tmpl);
2548     }
2549
2550 };
2551
2552 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2553
2554     title : 'test dialog',
2555
2556     buttons : false,
2557
2558     // set on load...
2559
2560     html: false,
2561
2562     tmp: false,
2563
2564     specificTitle: false,
2565
2566     buttonPosition: 'right',
2567
2568     allow_close : true,
2569
2570     animate : true,
2571
2572     fitwindow: false,
2573
2574
2575      // private
2576     dialogEl: false,
2577     bodyEl:  false,
2578     footerEl:  false,
2579     titleEl:  false,
2580     closeEl:  false,
2581
2582     size: '',
2583
2584
2585     onRender : function(ct, position)
2586     {
2587         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2588
2589         if(!this.el){
2590             var cfg = Roo.apply({},  this.getAutoCreate());
2591             cfg.id = Roo.id();
2592             //if(!cfg.name){
2593             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2594             //}
2595             //if (!cfg.name.length) {
2596             //    delete cfg.name;
2597            // }
2598             if (this.cls) {
2599                 cfg.cls += ' ' + this.cls;
2600             }
2601             if (this.style) {
2602                 cfg.style = this.style;
2603             }
2604             this.el = Roo.get(document.body).createChild(cfg, position);
2605         }
2606         //var type = this.el.dom.type;
2607
2608
2609         if(this.tabIndex !== undefined){
2610             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2611         }
2612
2613         this.dialogEl = this.el.select('.modal-dialog',true).first();
2614         this.bodyEl = this.el.select('.modal-body',true).first();
2615         this.closeEl = this.el.select('.modal-header .close', true).first();
2616         this.headerEl = this.el.select('.modal-header',true).first();
2617         this.titleEl = this.el.select('.modal-title',true).first();
2618         this.footerEl = this.el.select('.modal-footer',true).first();
2619
2620         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2621         this.maskEl.enableDisplayMode("block");
2622         this.maskEl.hide();
2623         //this.el.addClass("x-dlg-modal");
2624
2625         if (this.buttons.length) {
2626             Roo.each(this.buttons, function(bb) {
2627                 var b = Roo.apply({}, bb);
2628                 b.xns = b.xns || Roo.bootstrap;
2629                 b.xtype = b.xtype || 'Button';
2630                 if (typeof(b.listeners) == 'undefined') {
2631                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2632                 }
2633
2634                 var btn = Roo.factory(b);
2635
2636                 btn.render(this.el.select('.modal-footer div').first());
2637
2638             },this);
2639         }
2640         // render the children.
2641         var nitems = [];
2642
2643         if(typeof(this.items) != 'undefined'){
2644             var items = this.items;
2645             delete this.items;
2646
2647             for(var i =0;i < items.length;i++) {
2648                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2649             }
2650         }
2651
2652         this.items = nitems;
2653
2654         // where are these used - they used to be body/close/footer
2655
2656
2657         this.initEvents();
2658         //this.el.addClass([this.fieldClass, this.cls]);
2659
2660     },
2661
2662     getAutoCreate : function(){
2663
2664
2665         var bdy = {
2666                 cls : 'modal-body',
2667                 html : this.html || ''
2668         };
2669
2670         var title = {
2671             tag: 'h4',
2672             cls : 'modal-title',
2673             html : this.title
2674         };
2675
2676         if(this.specificTitle){
2677             title = this.title;
2678
2679         };
2680
2681         var header = [];
2682         if (this.allow_close) {
2683             header.push({
2684                 tag: 'button',
2685                 cls : 'close',
2686                 html : '&times'
2687             });
2688         }
2689
2690         header.push(title);
2691
2692         var size = '';
2693
2694         if(this.size.length){
2695             size = 'modal-' + this.size;
2696         }
2697
2698         var modal = {
2699             cls: "modal",
2700             style : 'display: none',
2701             cn : [
2702                 {
2703                     cls: "modal-dialog " + size,
2704                     cn : [
2705                         {
2706                             cls : "modal-content",
2707                             cn : [
2708                                 {
2709                                     cls : 'modal-header',
2710                                     cn : header
2711                                 },
2712                                 bdy,
2713                                 {
2714                                     cls : 'modal-footer',
2715                                     cn : [
2716                                         {
2717                                             tag: 'div',
2718                                             cls: 'btn-' + this.buttonPosition
2719                                         }
2720                                     ]
2721
2722                                 }
2723
2724
2725                             ]
2726
2727                         }
2728                     ]
2729
2730                 }
2731             ]
2732         };
2733
2734         if(this.animate){
2735             modal.cls += ' fade';
2736         }
2737
2738         return modal;
2739
2740     },
2741     getChildContainer : function() {
2742
2743          return this.bodyEl;
2744
2745     },
2746     getButtonContainer : function() {
2747          return this.el.select('.modal-footer div',true).first();
2748
2749     },
2750     initEvents : function()
2751     {
2752         if (this.allow_close) {
2753             this.closeEl.on('click', this.hide, this);
2754         }
2755         Roo.EventManager.onWindowResize(this.resize, this, true);
2756
2757
2758     },
2759
2760     resize : function()
2761     {
2762         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2763         if (this.fitwindow) {
2764             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2765             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2766             this.setSize(w,h);
2767         }
2768     },
2769
2770     setSize : function(w,h)
2771     {
2772         if (!w && !h) {
2773             return;
2774         }
2775         this.resizeTo(w,h);
2776     },
2777
2778     show : function() {
2779
2780         if (!this.rendered) {
2781             this.render();
2782         }
2783
2784         this.el.setStyle('display', 'block');
2785
2786         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2787             var _this = this;
2788             (function(){
2789                 this.el.addClass('in');
2790             }).defer(50, this);
2791         }else{
2792             this.el.addClass('in');
2793
2794         }
2795
2796         // not sure how we can show data in here..
2797         //if (this.tmpl) {
2798         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2799         //}
2800
2801         Roo.get(document.body).addClass("x-body-masked");
2802         
2803         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2804         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2805         this.maskEl.show();
2806         
2807         this.resize();
2808         
2809         this.fireEvent('show', this);
2810
2811         // set zindex here - otherwise it appears to be ignored...
2812         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2813
2814         (function () {
2815             this.items.forEach( function(e) {
2816                 e.layout ? e.layout() : false;
2817
2818             });
2819         }).defer(100,this);
2820
2821     },
2822     hide : function()
2823     {
2824         if(this.fireEvent("beforehide", this) !== false){
2825             this.maskEl.hide();
2826             Roo.get(document.body).removeClass("x-body-masked");
2827             this.el.removeClass('in');
2828             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2829
2830             if(this.animate){ // why
2831                 var _this = this;
2832                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2833             }else{
2834                 this.el.setStyle('display', 'none');
2835             }
2836             this.fireEvent('hide', this);
2837         }
2838     },
2839
2840     addButton : function(str, cb)
2841     {
2842
2843
2844         var b = Roo.apply({}, { html : str } );
2845         b.xns = b.xns || Roo.bootstrap;
2846         b.xtype = b.xtype || 'Button';
2847         if (typeof(b.listeners) == 'undefined') {
2848             b.listeners = { click : cb.createDelegate(this)  };
2849         }
2850
2851         var btn = Roo.factory(b);
2852
2853         btn.render(this.el.select('.modal-footer div').first());
2854
2855         return btn;
2856
2857     },
2858
2859     setDefaultButton : function(btn)
2860     {
2861         //this.el.select('.modal-footer').()
2862     },
2863     diff : false,
2864
2865     resizeTo: function(w,h)
2866     {
2867         // skip.. ?? why??
2868
2869         this.dialogEl.setWidth(w);
2870         if (this.diff === false) {
2871             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2872         }
2873
2874         this.bodyEl.setHeight(h-this.diff);
2875
2876         this.fireEvent('resize', this);
2877
2878     },
2879     setContentSize  : function(w, h)
2880     {
2881
2882     },
2883     onButtonClick: function(btn,e)
2884     {
2885         //Roo.log([a,b,c]);
2886         this.fireEvent('btnclick', btn.name, e);
2887     },
2888      /**
2889      * Set the title of the Dialog
2890      * @param {String} str new Title
2891      */
2892     setTitle: function(str) {
2893         this.titleEl.dom.innerHTML = str;
2894     },
2895     /**
2896      * Set the body of the Dialog
2897      * @param {String} str new Title
2898      */
2899     setBody: function(str) {
2900         this.bodyEl.dom.innerHTML = str;
2901     },
2902     /**
2903      * Set the body of the Dialog using the template
2904      * @param {Obj} data - apply this data to the template and replace the body contents.
2905      */
2906     applyBody: function(obj)
2907     {
2908         if (!this.tmpl) {
2909             Roo.log("Error - using apply Body without a template");
2910             //code
2911         }
2912         this.tmpl.overwrite(this.bodyEl, obj);
2913     }
2914
2915 });
2916
2917
2918 Roo.apply(Roo.bootstrap.Modal,  {
2919     /**
2920          * Button config that displays a single OK button
2921          * @type Object
2922          */
2923         OK :  [{
2924             name : 'ok',
2925             weight : 'primary',
2926             html : 'OK'
2927         }],
2928         /**
2929          * Button config that displays Yes and No buttons
2930          * @type Object
2931          */
2932         YESNO : [
2933             {
2934                 name  : 'no',
2935                 html : 'No'
2936             },
2937             {
2938                 name  :'yes',
2939                 weight : 'primary',
2940                 html : 'Yes'
2941             }
2942         ],
2943
2944         /**
2945          * Button config that displays OK and Cancel buttons
2946          * @type Object
2947          */
2948         OKCANCEL : [
2949             {
2950                name : 'cancel',
2951                 html : 'Cancel'
2952             },
2953             {
2954                 name : 'ok',
2955                 weight : 'primary',
2956                 html : 'OK'
2957             }
2958         ],
2959         /**
2960          * Button config that displays Yes, No and Cancel buttons
2961          * @type Object
2962          */
2963         YESNOCANCEL : [
2964             {
2965                 name : 'yes',
2966                 weight : 'primary',
2967                 html : 'Yes'
2968             },
2969             {
2970                 name : 'no',
2971                 html : 'No'
2972             },
2973             {
2974                 name : 'cancel',
2975                 html : 'Cancel'
2976             }
2977         ],
2978         
2979         zIndex : 10001
2980 });
2981 /*
2982  * - LGPL
2983  *
2984  * messagebox - can be used as a replace
2985  * 
2986  */
2987 /**
2988  * @class Roo.MessageBox
2989  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2990  * Example usage:
2991  *<pre><code>
2992 // Basic alert:
2993 Roo.Msg.alert('Status', 'Changes saved successfully.');
2994
2995 // Prompt for user data:
2996 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2997     if (btn == 'ok'){
2998         // process text value...
2999     }
3000 });
3001
3002 // Show a dialog using config options:
3003 Roo.Msg.show({
3004    title:'Save Changes?',
3005    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3006    buttons: Roo.Msg.YESNOCANCEL,
3007    fn: processResult,
3008    animEl: 'elId'
3009 });
3010 </code></pre>
3011  * @singleton
3012  */
3013 Roo.bootstrap.MessageBox = function(){
3014     var dlg, opt, mask, waitTimer;
3015     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3016     var buttons, activeTextEl, bwidth;
3017
3018     
3019     // private
3020     var handleButton = function(button){
3021         dlg.hide();
3022         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3023     };
3024
3025     // private
3026     var handleHide = function(){
3027         if(opt && opt.cls){
3028             dlg.el.removeClass(opt.cls);
3029         }
3030         //if(waitTimer){
3031         //    Roo.TaskMgr.stop(waitTimer);
3032         //    waitTimer = null;
3033         //}
3034     };
3035
3036     // private
3037     var updateButtons = function(b){
3038         var width = 0;
3039         if(!b){
3040             buttons["ok"].hide();
3041             buttons["cancel"].hide();
3042             buttons["yes"].hide();
3043             buttons["no"].hide();
3044             //dlg.footer.dom.style.display = 'none';
3045             return width;
3046         }
3047         dlg.footerEl.dom.style.display = '';
3048         for(var k in buttons){
3049             if(typeof buttons[k] != "function"){
3050                 if(b[k]){
3051                     buttons[k].show();
3052                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3053                     width += buttons[k].el.getWidth()+15;
3054                 }else{
3055                     buttons[k].hide();
3056                 }
3057             }
3058         }
3059         return width;
3060     };
3061
3062     // private
3063     var handleEsc = function(d, k, e){
3064         if(opt && opt.closable !== false){
3065             dlg.hide();
3066         }
3067         if(e){
3068             e.stopEvent();
3069         }
3070     };
3071
3072     return {
3073         /**
3074          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3075          * @return {Roo.BasicDialog} The BasicDialog element
3076          */
3077         getDialog : function(){
3078            if(!dlg){
3079                 dlg = new Roo.bootstrap.Modal( {
3080                     //draggable: true,
3081                     //resizable:false,
3082                     //constraintoviewport:false,
3083                     //fixedcenter:true,
3084                     //collapsible : false,
3085                     //shim:true,
3086                     //modal: true,
3087                 //    width: 'auto',
3088                   //  height:100,
3089                     //buttonAlign:"center",
3090                     closeClick : function(){
3091                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3092                             handleButton("no");
3093                         }else{
3094                             handleButton("cancel");
3095                         }
3096                     }
3097                 });
3098                 dlg.render();
3099                 dlg.on("hide", handleHide);
3100                 mask = dlg.mask;
3101                 //dlg.addKeyListener(27, handleEsc);
3102                 buttons = {};
3103                 this.buttons = buttons;
3104                 var bt = this.buttonText;
3105                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3106                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3107                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3108                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3109                 //Roo.log(buttons);
3110                 bodyEl = dlg.bodyEl.createChild({
3111
3112                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3113                         '<textarea class="roo-mb-textarea"></textarea>' +
3114                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3115                 });
3116                 msgEl = bodyEl.dom.firstChild;
3117                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3118                 textboxEl.enableDisplayMode();
3119                 textboxEl.addKeyListener([10,13], function(){
3120                     if(dlg.isVisible() && opt && opt.buttons){
3121                         if(opt.buttons.ok){
3122                             handleButton("ok");
3123                         }else if(opt.buttons.yes){
3124                             handleButton("yes");
3125                         }
3126                     }
3127                 });
3128                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3129                 textareaEl.enableDisplayMode();
3130                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3131                 progressEl.enableDisplayMode();
3132                 
3133                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3134                 var pf = progressEl.dom.firstChild;
3135                 if (pf) {
3136                     pp = Roo.get(pf.firstChild);
3137                     pp.setHeight(pf.offsetHeight);
3138                 }
3139                 
3140             }
3141             return dlg;
3142         },
3143
3144         /**
3145          * Updates the message box body text
3146          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3147          * the XHTML-compliant non-breaking space character '&amp;#160;')
3148          * @return {Roo.MessageBox} This message box
3149          */
3150         updateText : function(text)
3151         {
3152             if(!dlg.isVisible() && !opt.width){
3153                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3154                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3155             }
3156             msgEl.innerHTML = text || '&#160;';
3157       
3158             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3159             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3160             var w = Math.max(
3161                     Math.min(opt.width || cw , this.maxWidth), 
3162                     Math.max(opt.minWidth || this.minWidth, bwidth)
3163             );
3164             if(opt.prompt){
3165                 activeTextEl.setWidth(w);
3166             }
3167             if(dlg.isVisible()){
3168                 dlg.fixedcenter = false;
3169             }
3170             // to big, make it scroll. = But as usual stupid IE does not support
3171             // !important..
3172             
3173             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3174                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3175                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3176             } else {
3177                 bodyEl.dom.style.height = '';
3178                 bodyEl.dom.style.overflowY = '';
3179             }
3180             if (cw > w) {
3181                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3182             } else {
3183                 bodyEl.dom.style.overflowX = '';
3184             }
3185             
3186             dlg.setContentSize(w, bodyEl.getHeight());
3187             if(dlg.isVisible()){
3188                 dlg.fixedcenter = true;
3189             }
3190             return this;
3191         },
3192
3193         /**
3194          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3195          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3196          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3197          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3198          * @return {Roo.MessageBox} This message box
3199          */
3200         updateProgress : function(value, text){
3201             if(text){
3202                 this.updateText(text);
3203             }
3204             
3205             if (pp) { // weird bug on my firefox - for some reason this is not defined
3206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3207                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3208             }
3209             return this;
3210         },        
3211
3212         /**
3213          * Returns true if the message box is currently displayed
3214          * @return {Boolean} True if the message box is visible, else false
3215          */
3216         isVisible : function(){
3217             return dlg && dlg.isVisible();  
3218         },
3219
3220         /**
3221          * Hides the message box if it is displayed
3222          */
3223         hide : function(){
3224             if(this.isVisible()){
3225                 dlg.hide();
3226             }  
3227         },
3228
3229         /**
3230          * Displays a new message box, or reinitializes an existing message box, based on the config options
3231          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3232          * The following config object properties are supported:
3233          * <pre>
3234 Property    Type             Description
3235 ----------  ---------------  ------------------------------------------------------------------------------------
3236 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3237                                    closes (defaults to undefined)
3238 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3239                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3240 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3241                                    progress and wait dialogs will ignore this property and always hide the
3242                                    close button as they can only be closed programmatically.
3243 cls               String           A custom CSS class to apply to the message box element
3244 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3245                                    displayed (defaults to 75)
3246 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3247                                    function will be btn (the name of the button that was clicked, if applicable,
3248                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3249                                    Progress and wait dialogs will ignore this option since they do not respond to
3250                                    user actions and can only be closed programmatically, so any required function
3251                                    should be called by the same code after it closes the dialog.
3252 icon              String           A CSS class that provides a background image to be used as an icon for
3253                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3254 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3255 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3256 modal             Boolean          False to allow user interaction with the page while the message box is
3257                                    displayed (defaults to true)
3258 msg               String           A string that will replace the existing message box body text (defaults
3259                                    to the XHTML-compliant non-breaking space character '&#160;')
3260 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3261 progress          Boolean          True to display a progress bar (defaults to false)
3262 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3263 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3264 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3265 title             String           The title text
3266 value             String           The string value to set into the active textbox element if displayed
3267 wait              Boolean          True to display a progress bar (defaults to false)
3268 width             Number           The width of the dialog in pixels
3269 </pre>
3270          *
3271          * Example usage:
3272          * <pre><code>
3273 Roo.Msg.show({
3274    title: 'Address',
3275    msg: 'Please enter your address:',
3276    width: 300,
3277    buttons: Roo.MessageBox.OKCANCEL,
3278    multiline: true,
3279    fn: saveAddress,
3280    animEl: 'addAddressBtn'
3281 });
3282 </code></pre>
3283          * @param {Object} config Configuration options
3284          * @return {Roo.MessageBox} This message box
3285          */
3286         show : function(options)
3287         {
3288             
3289             // this causes nightmares if you show one dialog after another
3290             // especially on callbacks..
3291              
3292             if(this.isVisible()){
3293                 
3294                 this.hide();
3295                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3296                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3297                 Roo.log("New Dialog Message:" +  options.msg )
3298                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3299                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3300                 
3301             }
3302             var d = this.getDialog();
3303             opt = options;
3304             d.setTitle(opt.title || "&#160;");
3305             d.closeEl.setDisplayed(opt.closable !== false);
3306             activeTextEl = textboxEl;
3307             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3308             if(opt.prompt){
3309                 if(opt.multiline){
3310                     textboxEl.hide();
3311                     textareaEl.show();
3312                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3313                         opt.multiline : this.defaultTextHeight);
3314                     activeTextEl = textareaEl;
3315                 }else{
3316                     textboxEl.show();
3317                     textareaEl.hide();
3318                 }
3319             }else{
3320                 textboxEl.hide();
3321                 textareaEl.hide();
3322             }
3323             progressEl.setDisplayed(opt.progress === true);
3324             this.updateProgress(0);
3325             activeTextEl.dom.value = opt.value || "";
3326             if(opt.prompt){
3327                 dlg.setDefaultButton(activeTextEl);
3328             }else{
3329                 var bs = opt.buttons;
3330                 var db = null;
3331                 if(bs && bs.ok){
3332                     db = buttons["ok"];
3333                 }else if(bs && bs.yes){
3334                     db = buttons["yes"];
3335                 }
3336                 dlg.setDefaultButton(db);
3337             }
3338             bwidth = updateButtons(opt.buttons);
3339             this.updateText(opt.msg);
3340             if(opt.cls){
3341                 d.el.addClass(opt.cls);
3342             }
3343             d.proxyDrag = opt.proxyDrag === true;
3344             d.modal = opt.modal !== false;
3345             d.mask = opt.modal !== false ? mask : false;
3346             if(!d.isVisible()){
3347                 // force it to the end of the z-index stack so it gets a cursor in FF
3348                 document.body.appendChild(dlg.el.dom);
3349                 d.animateTarget = null;
3350                 d.show(options.animEl);
3351             }
3352             return this;
3353         },
3354
3355         /**
3356          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3357          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3358          * and closing the message box when the process is complete.
3359          * @param {String} title The title bar text
3360          * @param {String} msg The message box body text
3361          * @return {Roo.MessageBox} This message box
3362          */
3363         progress : function(title, msg){
3364             this.show({
3365                 title : title,
3366                 msg : msg,
3367                 buttons: false,
3368                 progress:true,
3369                 closable:false,
3370                 minWidth: this.minProgressWidth,
3371                 modal : true
3372             });
3373             return this;
3374         },
3375
3376         /**
3377          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3378          * If a callback function is passed it will be called after the user clicks the button, and the
3379          * id of the button that was clicked will be passed as the only parameter to the callback
3380          * (could also be the top-right close button).
3381          * @param {String} title The title bar text
3382          * @param {String} msg The message box body text
3383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3384          * @param {Object} scope (optional) The scope of the callback function
3385          * @return {Roo.MessageBox} This message box
3386          */
3387         alert : function(title, msg, fn, scope)
3388         {
3389             this.show({
3390                 title : title,
3391                 msg : msg,
3392                 buttons: this.OK,
3393                 fn: fn,
3394                 closable : false,
3395                 scope : scope,
3396                 modal : true
3397             });
3398             return this;
3399         },
3400
3401         /**
3402          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3403          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3404          * You are responsible for closing the message box when the process is complete.
3405          * @param {String} msg The message box body text
3406          * @param {String} title (optional) The title bar text
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         wait : function(msg, title){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: false,
3414                 closable:false,
3415                 progress:true,
3416                 modal:true,
3417                 width:300,
3418                 wait:true
3419             });
3420             waitTimer = Roo.TaskMgr.start({
3421                 run: function(i){
3422                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3423                 },
3424                 interval: 1000
3425             });
3426             return this;
3427         },
3428
3429         /**
3430          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3431          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3432          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3433          * @param {String} title The title bar text
3434          * @param {String} msg The message box body text
3435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3436          * @param {Object} scope (optional) The scope of the callback function
3437          * @return {Roo.MessageBox} This message box
3438          */
3439         confirm : function(title, msg, fn, scope){
3440             this.show({
3441                 title : title,
3442                 msg : msg,
3443                 buttons: this.YESNO,
3444                 fn: fn,
3445                 scope : scope,
3446                 modal : true
3447             });
3448             return this;
3449         },
3450
3451         /**
3452          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3453          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3454          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3455          * (could also be the top-right close button) and the text that was entered will be passed as the two
3456          * parameters to the callback.
3457          * @param {String} title The title bar text
3458          * @param {String} msg The message box body text
3459          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3460          * @param {Object} scope (optional) The scope of the callback function
3461          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3462          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3463          * @return {Roo.MessageBox} This message box
3464          */
3465         prompt : function(title, msg, fn, scope, multiline){
3466             this.show({
3467                 title : title,
3468                 msg : msg,
3469                 buttons: this.OKCANCEL,
3470                 fn: fn,
3471                 minWidth:250,
3472                 scope : scope,
3473                 prompt:true,
3474                 multiline: multiline,
3475                 modal : true
3476             });
3477             return this;
3478         },
3479
3480         /**
3481          * Button config that displays a single OK button
3482          * @type Object
3483          */
3484         OK : {ok:true},
3485         /**
3486          * Button config that displays Yes and No buttons
3487          * @type Object
3488          */
3489         YESNO : {yes:true, no:true},
3490         /**
3491          * Button config that displays OK and Cancel buttons
3492          * @type Object
3493          */
3494         OKCANCEL : {ok:true, cancel:true},
3495         /**
3496          * Button config that displays Yes, No and Cancel buttons
3497          * @type Object
3498          */
3499         YESNOCANCEL : {yes:true, no:true, cancel:true},
3500
3501         /**
3502          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3503          * @type Number
3504          */
3505         defaultTextHeight : 75,
3506         /**
3507          * The maximum width in pixels of the message box (defaults to 600)
3508          * @type Number
3509          */
3510         maxWidth : 600,
3511         /**
3512          * The minimum width in pixels of the message box (defaults to 100)
3513          * @type Number
3514          */
3515         minWidth : 100,
3516         /**
3517          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3518          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3519          * @type Number
3520          */
3521         minProgressWidth : 250,
3522         /**
3523          * An object containing the default button text strings that can be overriden for localized language support.
3524          * Supported properties are: ok, cancel, yes and no.
3525          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3526          * @type Object
3527          */
3528         buttonText : {
3529             ok : "OK",
3530             cancel : "Cancel",
3531             yes : "Yes",
3532             no : "No"
3533         }
3534     };
3535 }();
3536
3537 /**
3538  * Shorthand for {@link Roo.MessageBox}
3539  */
3540 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3541 Roo.Msg = Roo.Msg || Roo.MessageBox;
3542 /*
3543  * - LGPL
3544  *
3545  * navbar
3546  * 
3547  */
3548
3549 /**
3550  * @class Roo.bootstrap.Navbar
3551  * @extends Roo.bootstrap.Component
3552  * Bootstrap Navbar class
3553
3554  * @constructor
3555  * Create a new Navbar
3556  * @param {Object} config The config object
3557  */
3558
3559
3560 Roo.bootstrap.Navbar = function(config){
3561     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3562     this.addEvents({
3563         // raw events
3564         /**
3565          * @event beforetoggle
3566          * Fire before toggle the menu
3567          * @param {Roo.EventObject} e
3568          */
3569         "beforetoggle" : true
3570     });
3571 };
3572
3573 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3574     
3575     
3576    
3577     // private
3578     navItems : false,
3579     loadMask : false,
3580     
3581     
3582     getAutoCreate : function(){
3583         
3584         
3585         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3586         
3587     },
3588     
3589     initEvents :function ()
3590     {
3591         //Roo.log(this.el.select('.navbar-toggle',true));
3592         this.el.select('.navbar-toggle',true).on('click', function() {
3593             if(this.fireEvent('beforetoggle', this) !== false){
3594                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3595             }
3596             
3597         }, this);
3598         
3599         var mark = {
3600             tag: "div",
3601             cls:"x-dlg-mask"
3602         };
3603         
3604         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3605         
3606         var size = this.el.getSize();
3607         this.maskEl.setSize(size.width, size.height);
3608         this.maskEl.enableDisplayMode("block");
3609         this.maskEl.hide();
3610         
3611         if(this.loadMask){
3612             this.maskEl.show();
3613         }
3614     },
3615     
3616     
3617     getChildContainer : function()
3618     {
3619         if (this.el.select('.collapse').getCount()) {
3620             return this.el.select('.collapse',true).first();
3621         }
3622         
3623         return this.el;
3624     },
3625     
3626     mask : function()
3627     {
3628         this.maskEl.show();
3629     },
3630     
3631     unmask : function()
3632     {
3633         this.maskEl.hide();
3634     } 
3635     
3636     
3637     
3638     
3639 });
3640
3641
3642
3643  
3644
3645  /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSimplebar
3654  * @extends Roo.bootstrap.Navbar
3655  * Bootstrap Sidebar class
3656  *
3657  * @cfg {Boolean} inverse is inverted color
3658  * 
3659  * @cfg {String} type (nav | pills | tabs)
3660  * @cfg {Boolean} arrangement stacked | justified
3661  * @cfg {String} align (left | right) alignment
3662  * 
3663  * @cfg {Boolean} main (true|false) main nav bar? default false
3664  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3665  * 
3666  * @cfg {String} tag (header|footer|nav|div) default is nav 
3667
3668  * 
3669  * 
3670  * 
3671  * @constructor
3672  * Create a new Sidebar
3673  * @param {Object} config The config object
3674  */
3675
3676
3677 Roo.bootstrap.NavSimplebar = function(config){
3678     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3682     
3683     inverse: false,
3684     
3685     type: false,
3686     arrangement: '',
3687     align : false,
3688     
3689     
3690     
3691     main : false,
3692     
3693     
3694     tag : false,
3695     
3696     
3697     getAutoCreate : function(){
3698         
3699         
3700         var cfg = {
3701             tag : this.tag || 'div',
3702             cls : 'navbar'
3703         };
3704           
3705         
3706         cfg.cn = [
3707             {
3708                 cls: 'nav',
3709                 tag : 'ul'
3710             }
3711         ];
3712         
3713          
3714         this.type = this.type || 'nav';
3715         if (['tabs','pills'].indexOf(this.type)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.type
3717         
3718         
3719         } else {
3720             if (this.type!=='nav') {
3721                 Roo.log('nav type must be nav/tabs/pills')
3722             }
3723             cfg.cn[0].cls += ' navbar-nav'
3724         }
3725         
3726         
3727         
3728         
3729         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3730             cfg.cn[0].cls += ' nav-' + this.arrangement;
3731         }
3732         
3733         
3734         if (this.align === 'right') {
3735             cfg.cn[0].cls += ' navbar-right';
3736         }
3737         
3738         if (this.inverse) {
3739             cfg.cls += ' navbar-inverse';
3740             
3741         }
3742         
3743         
3744         return cfg;
3745     
3746         
3747     }
3748     
3749     
3750     
3751 });
3752
3753
3754
3755  
3756
3757  
3758        /*
3759  * - LGPL
3760  *
3761  * navbar
3762  * 
3763  */
3764
3765 /**
3766  * @class Roo.bootstrap.NavHeaderbar
3767  * @extends Roo.bootstrap.NavSimplebar
3768  * Bootstrap Sidebar class
3769  *
3770  * @cfg {String} brand what is brand
3771  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3772  * @cfg {String} brand_href href of the brand
3773  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3774  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3775  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3776  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3777  * 
3778  * @constructor
3779  * Create a new Sidebar
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.NavHeaderbar = function(config){
3785     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3786       
3787 };
3788
3789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3790     
3791     position: '',
3792     brand: '',
3793     brand_href: false,
3794     srButton : true,
3795     autohide : false,
3796     desktopCenter : false,
3797    
3798     
3799     getAutoCreate : function(){
3800         
3801         var   cfg = {
3802             tag: this.nav || 'nav',
3803             cls: 'navbar',
3804             role: 'navigation',
3805             cn: []
3806         };
3807         
3808         var cn = cfg.cn;
3809         if (this.desktopCenter) {
3810             cn.push({cls : 'container', cn : []});
3811             cn = cn[0].cn;
3812         }
3813         
3814         if(this.srButton){
3815             cn.push({
3816                 tag: 'div',
3817                 cls: 'navbar-header',
3818                 cn: [
3819                     {
3820                         tag: 'button',
3821                         type: 'button',
3822                         cls: 'navbar-toggle',
3823                         'data-toggle': 'collapse',
3824                         cn: [
3825                             {
3826                                 tag: 'span',
3827                                 cls: 'sr-only',
3828                                 html: 'Toggle navigation'
3829                             },
3830                             {
3831                                 tag: 'span',
3832                                 cls: 'icon-bar'
3833                             },
3834                             {
3835                                 tag: 'span',
3836                                 cls: 'icon-bar'
3837                             },
3838                             {
3839                                 tag: 'span',
3840                                 cls: 'icon-bar'
3841                             }
3842                         ]
3843                     }
3844                 ]
3845             });
3846         }
3847         
3848         cn.push({
3849             tag: 'div',
3850             cls: 'collapse navbar-collapse',
3851             cn : []
3852         });
3853         
3854         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3855         
3856         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3857             cfg.cls += ' navbar-' + this.position;
3858             
3859             // tag can override this..
3860             
3861             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3862         }
3863         
3864         if (this.brand !== '') {
3865             cn[0].cn.push({
3866                 tag: 'a',
3867                 href: this.brand_href ? this.brand_href : '#',
3868                 cls: 'navbar-brand',
3869                 cn: [
3870                 this.brand
3871                 ]
3872             });
3873         }
3874         
3875         if(this.main){
3876             cfg.cls += ' main-nav';
3877         }
3878         
3879         
3880         return cfg;
3881
3882         
3883     },
3884     getHeaderChildContainer : function()
3885     {
3886         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3887             return this.el.select('.navbar-header',true).first();
3888         }
3889         
3890         return this.getChildContainer();
3891     },
3892     
3893     
3894     initEvents : function()
3895     {
3896         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3897         
3898         if (this.autohide) {
3899             
3900             var prevScroll = 0;
3901             var ft = this.el;
3902             
3903             Roo.get(document).on('scroll',function(e) {
3904                 var ns = Roo.get(document).getScroll().top;
3905                 var os = prevScroll;
3906                 prevScroll = ns;
3907                 
3908                 if(ns > os){
3909                     ft.removeClass('slideDown');
3910                     ft.addClass('slideUp');
3911                     return;
3912                 }
3913                 ft.removeClass('slideUp');
3914                 ft.addClass('slideDown');
3915                  
3916               
3917           },this);
3918         }
3919     }    
3920     
3921 });
3922
3923
3924
3925  
3926
3927  /*
3928  * - LGPL
3929  *
3930  * navbar
3931  * 
3932  */
3933
3934 /**
3935  * @class Roo.bootstrap.NavSidebar
3936  * @extends Roo.bootstrap.Navbar
3937  * Bootstrap Sidebar class
3938  * 
3939  * @constructor
3940  * Create a new Sidebar
3941  * @param {Object} config The config object
3942  */
3943
3944
3945 Roo.bootstrap.NavSidebar = function(config){
3946     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3947 };
3948
3949 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3950     
3951     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3952     
3953     getAutoCreate : function(){
3954         
3955         
3956         return  {
3957             tag: 'div',
3958             cls: 'sidebar sidebar-nav'
3959         };
3960     
3961         
3962     }
3963     
3964     
3965     
3966 });
3967
3968
3969
3970  
3971
3972  /*
3973  * - LGPL
3974  *
3975  * nav group
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavGroup
3981  * @extends Roo.bootstrap.Component
3982  * Bootstrap NavGroup class
3983  * @cfg {String} align (left|right)
3984  * @cfg {Boolean} inverse
3985  * @cfg {String} type (nav|pills|tab) default nav
3986  * @cfg {String} navId - reference Id for navbar.
3987
3988  * 
3989  * @constructor
3990  * Create a new nav group
3991  * @param {Object} config The config object
3992  */
3993
3994 Roo.bootstrap.NavGroup = function(config){
3995     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3996     this.navItems = [];
3997    
3998     Roo.bootstrap.NavGroup.register(this);
3999      this.addEvents({
4000         /**
4001              * @event changed
4002              * Fires when the active item changes
4003              * @param {Roo.bootstrap.NavGroup} this
4004              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4005              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4006          */
4007         'changed': true
4008      });
4009     
4010 };
4011
4012 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4013     
4014     align: '',
4015     inverse: false,
4016     form: false,
4017     type: 'nav',
4018     navId : '',
4019     // private
4020     
4021     navItems : false, 
4022     
4023     getAutoCreate : function()
4024     {
4025         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4026         
4027         cfg = {
4028             tag : 'ul',
4029             cls: 'nav' 
4030         };
4031         
4032         if (['tabs','pills'].indexOf(this.type)!==-1) {
4033             cfg.cls += ' nav-' + this.type
4034         } else {
4035             if (this.type!=='nav') {
4036                 Roo.log('nav type must be nav/tabs/pills')
4037             }
4038             cfg.cls += ' navbar-nav'
4039         }
4040         
4041         if (this.parent() && this.parent().sidebar) {
4042             cfg = {
4043                 tag: 'ul',
4044                 cls: 'dashboard-menu sidebar-menu'
4045             };
4046             
4047             return cfg;
4048         }
4049         
4050         if (this.form === true) {
4051             cfg = {
4052                 tag: 'form',
4053                 cls: 'navbar-form'
4054             };
4055             
4056             if (this.align === 'right') {
4057                 cfg.cls += ' navbar-right';
4058             } else {
4059                 cfg.cls += ' navbar-left';
4060             }
4061         }
4062         
4063         if (this.align === 'right') {
4064             cfg.cls += ' navbar-right';
4065         }
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         
4073         return cfg;
4074     },
4075     /**
4076     * sets the active Navigation item
4077     * @param {Roo.bootstrap.NavItem} the new current navitem
4078     */
4079     setActiveItem : function(item)
4080     {
4081         var prev = false;
4082         Roo.each(this.navItems, function(v){
4083             if (v == item) {
4084                 return ;
4085             }
4086             if (v.isActive()) {
4087                 v.setActive(false, true);
4088                 prev = v;
4089                 
4090             }
4091             
4092         });
4093
4094         item.setActive(true, true);
4095         this.fireEvent('changed', this, item, prev);
4096         
4097         
4098     },
4099     /**
4100     * gets the active Navigation item
4101     * @return {Roo.bootstrap.NavItem} the current navitem
4102     */
4103     getActive : function()
4104     {
4105         
4106         var prev = false;
4107         Roo.each(this.navItems, function(v){
4108             
4109             if (v.isActive()) {
4110                 prev = v;
4111                 
4112             }
4113             
4114         });
4115         return prev;
4116     },
4117     
4118     indexOfNav : function()
4119     {
4120         
4121         var prev = false;
4122         Roo.each(this.navItems, function(v,i){
4123             
4124             if (v.isActive()) {
4125                 prev = i;
4126                 
4127             }
4128             
4129         });
4130         return prev;
4131     },
4132     /**
4133     * adds a Navigation item
4134     * @param {Roo.bootstrap.NavItem} the navitem to add
4135     */
4136     addItem : function(cfg)
4137     {
4138         var cn = new Roo.bootstrap.NavItem(cfg);
4139         this.register(cn);
4140         cn.parentId = this.id;
4141         cn.onRender(this.el, null);
4142         return cn;
4143     },
4144     /**
4145     * register a Navigation item
4146     * @param {Roo.bootstrap.NavItem} the navitem to add
4147     */
4148     register : function(item)
4149     {
4150         this.navItems.push( item);
4151         item.navId = this.navId;
4152     
4153     },
4154     
4155     /**
4156     * clear all the Navigation item
4157     */
4158    
4159     clearAll : function()
4160     {
4161         this.navItems = [];
4162         this.el.dom.innerHTML = '';
4163     },
4164     
4165     getNavItem: function(tabId)
4166     {
4167         var ret = false;
4168         Roo.each(this.navItems, function(e) {
4169             if (e.tabId == tabId) {
4170                ret =  e;
4171                return false;
4172             }
4173             return true;
4174             
4175         });
4176         return ret;
4177     },
4178     
4179     setActiveNext : function()
4180     {
4181         var i = this.indexOfNav(this.getActive());
4182         if (i > this.navItems.length) {
4183             return;
4184         }
4185         this.setActiveItem(this.navItems[i+1]);
4186     },
4187     setActivePrev : function()
4188     {
4189         var i = this.indexOfNav(this.getActive());
4190         if (i  < 1) {
4191             return;
4192         }
4193         this.setActiveItem(this.navItems[i-1]);
4194     },
4195     clearWasActive : function(except) {
4196         Roo.each(this.navItems, function(e) {
4197             if (e.tabId != except.tabId && e.was_active) {
4198                e.was_active = false;
4199                return false;
4200             }
4201             return true;
4202             
4203         });
4204     },
4205     getWasActive : function ()
4206     {
4207         var r = false;
4208         Roo.each(this.navItems, function(e) {
4209             if (e.was_active) {
4210                r = e;
4211                return false;
4212             }
4213             return true;
4214             
4215         });
4216         return r;
4217     }
4218     
4219     
4220 });
4221
4222  
4223 Roo.apply(Roo.bootstrap.NavGroup, {
4224     
4225     groups: {},
4226      /**
4227     * register a Navigation Group
4228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4229     */
4230     register : function(navgrp)
4231     {
4232         this.groups[navgrp.navId] = navgrp;
4233         
4234     },
4235     /**
4236     * fetch a Navigation Group based on the navigation ID
4237     * @param {string} the navgroup to add
4238     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4239     */
4240     get: function(navId) {
4241         if (typeof(this.groups[navId]) == 'undefined') {
4242             return false;
4243             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4244         }
4245         return this.groups[navId] ;
4246     }
4247     
4248     
4249     
4250 });
4251
4252  /*
4253  * - LGPL
4254  *
4255  * row
4256  * 
4257  */
4258
4259 /**
4260  * @class Roo.bootstrap.NavItem
4261  * @extends Roo.bootstrap.Component
4262  * Bootstrap Navbar.NavItem class
4263  * @cfg {String} href  link to
4264  * @cfg {String} html content of button
4265  * @cfg {String} badge text inside badge
4266  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4267  * @cfg {String} glyphicon name of glyphicon
4268  * @cfg {String} icon name of font awesome icon
4269  * @cfg {Boolean} active Is item active
4270  * @cfg {Boolean} disabled Is item disabled
4271  
4272  * @cfg {Boolean} preventDefault (true | false) default false
4273  * @cfg {String} tabId the tab that this item activates.
4274  * @cfg {String} tagtype (a|span) render as a href or span?
4275  * @cfg {Boolean} animateRef (true|false) link to element default false  
4276   
4277  * @constructor
4278  * Create a new Navbar Item
4279  * @param {Object} config The config object
4280  */
4281 Roo.bootstrap.NavItem = function(config){
4282     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4283     this.addEvents({
4284         // raw events
4285         /**
4286          * @event click
4287          * The raw click event for the entire grid.
4288          * @param {Roo.EventObject} e
4289          */
4290         "click" : true,
4291          /**
4292             * @event changed
4293             * Fires when the active item active state changes
4294             * @param {Roo.bootstrap.NavItem} this
4295             * @param {boolean} state the new state
4296              
4297          */
4298         'changed': true,
4299         /**
4300             * @event scrollto
4301             * Fires when scroll to element
4302             * @param {Roo.bootstrap.NavItem} this
4303             * @param {Object} options
4304             * @param {Roo.EventObject} e
4305              
4306          */
4307         'scrollto': true
4308     });
4309    
4310 };
4311
4312 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4313     
4314     href: false,
4315     html: '',
4316     badge: '',
4317     icon: false,
4318     glyphicon: false,
4319     active: false,
4320     preventDefault : false,
4321     tabId : false,
4322     tagtype : 'a',
4323     disabled : false,
4324     animateRef : false,
4325     was_active : false,
4326     
4327     getAutoCreate : function(){
4328          
4329         var cfg = {
4330             tag: 'li',
4331             cls: 'nav-item'
4332             
4333         };
4334         
4335         if (this.active) {
4336             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4337         }
4338         if (this.disabled) {
4339             cfg.cls += ' disabled';
4340         }
4341         
4342         if (this.href || this.html || this.glyphicon || this.icon) {
4343             cfg.cn = [
4344                 {
4345                     tag: this.tagtype,
4346                     href : this.href || "#",
4347                     html: this.html || ''
4348                 }
4349             ];
4350             
4351             if (this.icon) {
4352                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4353             }
4354
4355             if(this.glyphicon) {
4356                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4357             }
4358             
4359             if (this.menu) {
4360                 
4361                 cfg.cn[0].html += " <span class='caret'></span>";
4362              
4363             }
4364             
4365             if (this.badge !== '') {
4366                  
4367                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4368             }
4369         }
4370         
4371         
4372         
4373         return cfg;
4374     },
4375     initEvents: function() 
4376     {
4377         if (typeof (this.menu) != 'undefined') {
4378             this.menu.parentType = this.xtype;
4379             this.menu.triggerEl = this.el;
4380             this.menu = this.addxtype(Roo.apply({}, this.menu));
4381         }
4382         
4383         this.el.select('a',true).on('click', this.onClick, this);
4384         
4385         if(this.tagtype == 'span'){
4386             this.el.select('span',true).on('click', this.onClick, this);
4387         }
4388        
4389         // at this point parent should be available..
4390         this.parent().register(this);
4391     },
4392     
4393     onClick : function(e)
4394     {
4395         if (e.getTarget('.dropdown-menu-item')) {
4396             // did you click on a menu itemm.... - then don't trigger onclick..
4397             return;
4398         }
4399         
4400         if(
4401                 this.preventDefault || 
4402                 this.href == '#' 
4403         ){
4404             Roo.log("NavItem - prevent Default?");
4405             e.preventDefault();
4406         }
4407         
4408         if (this.disabled) {
4409             return;
4410         }
4411         
4412         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4413         if (tg && tg.transition) {
4414             Roo.log("waiting for the transitionend");
4415             return;
4416         }
4417         
4418         
4419         
4420         //Roo.log("fire event clicked");
4421         if(this.fireEvent('click', this, e) === false){
4422             return;
4423         };
4424         
4425         if(this.tagtype == 'span'){
4426             return;
4427         }
4428         
4429         //Roo.log(this.href);
4430         var ael = this.el.select('a',true).first();
4431         //Roo.log(ael);
4432         
4433         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4434             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4435             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4436                 return; // ignore... - it's a 'hash' to another page.
4437             }
4438             Roo.log("NavItem - prevent Default?");
4439             e.preventDefault();
4440             this.scrollToElement(e);
4441         }
4442         
4443         
4444         var p =  this.parent();
4445    
4446         if (['tabs','pills'].indexOf(p.type)!==-1) {
4447             if (typeof(p.setActiveItem) !== 'undefined') {
4448                 p.setActiveItem(this);
4449             }
4450         }
4451         
4452         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4453         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4454             // remove the collapsed menu expand...
4455             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4456         }
4457     },
4458     
4459     isActive: function () {
4460         return this.active
4461     },
4462     setActive : function(state, fire, is_was_active)
4463     {
4464         if (this.active && !state && this.navId) {
4465             this.was_active = true;
4466             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4467             if (nv) {
4468                 nv.clearWasActive(this);
4469             }
4470             
4471         }
4472         this.active = state;
4473         
4474         if (!state ) {
4475             this.el.removeClass('active');
4476         } else if (!this.el.hasClass('active')) {
4477             this.el.addClass('active');
4478         }
4479         if (fire) {
4480             this.fireEvent('changed', this, state);
4481         }
4482         
4483         // show a panel if it's registered and related..
4484         
4485         if (!this.navId || !this.tabId || !state || is_was_active) {
4486             return;
4487         }
4488         
4489         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4490         if (!tg) {
4491             return;
4492         }
4493         var pan = tg.getPanelByName(this.tabId);
4494         if (!pan) {
4495             return;
4496         }
4497         // if we can not flip to new panel - go back to old nav highlight..
4498         if (false == tg.showPanel(pan)) {
4499             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4500             if (nv) {
4501                 var onav = nv.getWasActive();
4502                 if (onav) {
4503                     onav.setActive(true, false, true);
4504                 }
4505             }
4506             
4507         }
4508         
4509         
4510         
4511     },
4512      // this should not be here...
4513     setDisabled : function(state)
4514     {
4515         this.disabled = state;
4516         if (!state ) {
4517             this.el.removeClass('disabled');
4518         } else if (!this.el.hasClass('disabled')) {
4519             this.el.addClass('disabled');
4520         }
4521         
4522     },
4523     
4524     /**
4525      * Fetch the element to display the tooltip on.
4526      * @return {Roo.Element} defaults to this.el
4527      */
4528     tooltipEl : function()
4529     {
4530         return this.el.select('' + this.tagtype + '', true).first();
4531     },
4532     
4533     scrollToElement : function(e)
4534     {
4535         var c = document.body;
4536         
4537         /*
4538          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4539          */
4540         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4541             c = document.documentElement;
4542         }
4543         
4544         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4545         
4546         if(!target){
4547             return;
4548         }
4549
4550         var o = target.calcOffsetsTo(c);
4551         
4552         var options = {
4553             target : target,
4554             value : o[1]
4555         };
4556         
4557         this.fireEvent('scrollto', this, options, e);
4558         
4559         Roo.get(c).scrollTo('top', options.value, true);
4560         
4561         return;
4562     }
4563 });
4564  
4565
4566  /*
4567  * - LGPL
4568  *
4569  * sidebar item
4570  *
4571  *  li
4572  *    <span> icon </span>
4573  *    <span> text </span>
4574  *    <span>badge </span>
4575  */
4576
4577 /**
4578  * @class Roo.bootstrap.NavSidebarItem
4579  * @extends Roo.bootstrap.NavItem
4580  * Bootstrap Navbar.NavSidebarItem class
4581  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4582  * {Boolean} open is the menu open
4583  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4584  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4585  * {String} buttonSize (sm|md|lg)the extra classes for the button
4586  * {Boolean} showArrow show arrow next to the text (default true)
4587  * @constructor
4588  * Create a new Navbar Button
4589  * @param {Object} config The config object
4590  */
4591 Roo.bootstrap.NavSidebarItem = function(config){
4592     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4593     this.addEvents({
4594         // raw events
4595         /**
4596          * @event click
4597          * The raw click event for the entire grid.
4598          * @param {Roo.EventObject} e
4599          */
4600         "click" : true,
4601          /**
4602             * @event changed
4603             * Fires when the active item active state changes
4604             * @param {Roo.bootstrap.NavSidebarItem} this
4605             * @param {boolean} state the new state
4606              
4607          */
4608         'changed': true
4609     });
4610    
4611 };
4612
4613 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4614     
4615     badgeWeight : 'default',
4616     
4617     open: false,
4618     
4619     buttonView : false,
4620     
4621     buttonWeight : 'default',
4622     
4623     buttonSize : 'md',
4624     
4625     showArrow : true,
4626     
4627     getAutoCreate : function(){
4628         
4629         
4630         var a = {
4631                 tag: 'a',
4632                 href : this.href || '#',
4633                 cls: '',
4634                 html : '',
4635                 cn : []
4636         };
4637         
4638         if(this.buttonView){
4639             a = {
4640                 tag: 'button',
4641                 href : this.href || '#',
4642                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4643                 html : this.html,
4644                 cn : []
4645             };
4646         }
4647         
4648         var cfg = {
4649             tag: 'li',
4650             cls: '',
4651             cn: [ a ]
4652         };
4653         
4654         if (this.active) {
4655             cfg.cls += ' active';
4656         }
4657         
4658         if (this.disabled) {
4659             cfg.cls += ' disabled';
4660         }
4661         if (this.open) {
4662             cfg.cls += ' open x-open';
4663         }
4664         // left icon..
4665         if (this.glyphicon || this.icon) {
4666             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4667             a.cn.push({ tag : 'i', cls : c }) ;
4668         }
4669         
4670         if(!this.buttonView){
4671             var span = {
4672                 tag: 'span',
4673                 html : this.html || ''
4674             };
4675
4676             a.cn.push(span);
4677             
4678         }
4679         
4680         if (this.badge !== '') {
4681             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4682         }
4683         
4684         if (this.menu) {
4685             
4686             if(this.showArrow){
4687                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4688             }
4689             
4690             a.cls += ' dropdown-toggle treeview' ;
4691         }
4692         
4693         return cfg;
4694     },
4695     
4696     initEvents : function()
4697     { 
4698         if (typeof (this.menu) != 'undefined') {
4699             this.menu.parentType = this.xtype;
4700             this.menu.triggerEl = this.el;
4701             this.menu = this.addxtype(Roo.apply({}, this.menu));
4702         }
4703         
4704         this.el.on('click', this.onClick, this);
4705         
4706         if(this.badge !== ''){
4707             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4708         }
4709         
4710     },
4711     
4712     onClick : function(e)
4713     {
4714         if(this.disabled){
4715             e.preventDefault();
4716             return;
4717         }
4718         
4719         if(this.preventDefault){
4720             e.preventDefault();
4721         }
4722         
4723         this.fireEvent('click', this);
4724     },
4725     
4726     disable : function()
4727     {
4728         this.setDisabled(true);
4729     },
4730     
4731     enable : function()
4732     {
4733         this.setDisabled(false);
4734     },
4735     
4736     setDisabled : function(state)
4737     {
4738         if(this.disabled == state){
4739             return;
4740         }
4741         
4742         this.disabled = state;
4743         
4744         if (state) {
4745             this.el.addClass('disabled');
4746             return;
4747         }
4748         
4749         this.el.removeClass('disabled');
4750         
4751         return;
4752     },
4753     
4754     setActive : function(state)
4755     {
4756         if(this.active == state){
4757             return;
4758         }
4759         
4760         this.active = state;
4761         
4762         if (state) {
4763             this.el.addClass('active');
4764             return;
4765         }
4766         
4767         this.el.removeClass('active');
4768         
4769         return;
4770     },
4771     
4772     isActive: function () 
4773     {
4774         return this.active;
4775     },
4776     
4777     setBadge : function(str)
4778     {
4779         if(!this.badgeEl){
4780             return;
4781         }
4782         
4783         this.badgeEl.dom.innerHTML = str;
4784     }
4785     
4786    
4787      
4788  
4789 });
4790  
4791
4792  /*
4793  * - LGPL
4794  *
4795  * row
4796  * 
4797  */
4798
4799 /**
4800  * @class Roo.bootstrap.Row
4801  * @extends Roo.bootstrap.Component
4802  * Bootstrap Row class (contains columns...)
4803  * 
4804  * @constructor
4805  * Create a new Row
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Row = function(config){
4810     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4811 };
4812
4813 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4814     
4815     getAutoCreate : function(){
4816        return {
4817             cls: 'row clearfix'
4818        };
4819     }
4820     
4821     
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * element
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Element
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Element class
4837  * @cfg {String} html contents of the element
4838  * @cfg {String} tag tag of the element
4839  * @cfg {String} cls class of the element
4840  * @cfg {Boolean} preventDefault (true|false) default false
4841  * @cfg {Boolean} clickable (true|false) default false
4842  * 
4843  * @constructor
4844  * Create a new Element
4845  * @param {Object} config The config object
4846  */
4847
4848 Roo.bootstrap.Element = function(config){
4849     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4850     
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * When a element is chick
4856          * @param {Roo.bootstrap.Element} this
4857          * @param {Roo.EventObject} e
4858          */
4859         "click" : true
4860     });
4861 };
4862
4863 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4864     
4865     tag: 'div',
4866     cls: '',
4867     html: '',
4868     preventDefault: false, 
4869     clickable: false,
4870     
4871     getAutoCreate : function(){
4872         
4873         var cfg = {
4874             tag: this.tag,
4875             cls: this.cls,
4876             html: this.html
4877         };
4878         
4879         return cfg;
4880     },
4881     
4882     initEvents: function() 
4883     {
4884         Roo.bootstrap.Element.superclass.initEvents.call(this);
4885         
4886         if(this.clickable){
4887             this.el.on('click', this.onClick, this);
4888         }
4889         
4890     },
4891     
4892     onClick : function(e)
4893     {
4894         if(this.preventDefault){
4895             e.preventDefault();
4896         }
4897         
4898         this.fireEvent('click', this, e);
4899     },
4900     
4901     getValue : function()
4902     {
4903         return this.el.dom.innerHTML;
4904     },
4905     
4906     setValue : function(value)
4907     {
4908         this.el.dom.innerHTML = value;
4909     }
4910    
4911 });
4912
4913  
4914
4915  /*
4916  * - LGPL
4917  *
4918  * pagination
4919  * 
4920  */
4921
4922 /**
4923  * @class Roo.bootstrap.Pagination
4924  * @extends Roo.bootstrap.Component
4925  * Bootstrap Pagination class
4926  * @cfg {String} size xs | sm | md | lg
4927  * @cfg {Boolean} inverse false | true
4928  * 
4929  * @constructor
4930  * Create a new Pagination
4931  * @param {Object} config The config object
4932  */
4933
4934 Roo.bootstrap.Pagination = function(config){
4935     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4936 };
4937
4938 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4939     
4940     cls: false,
4941     size: false,
4942     inverse: false,
4943     
4944     getAutoCreate : function(){
4945         var cfg = {
4946             tag: 'ul',
4947                 cls: 'pagination'
4948         };
4949         if (this.inverse) {
4950             cfg.cls += ' inverse';
4951         }
4952         if (this.html) {
4953             cfg.html=this.html;
4954         }
4955         if (this.cls) {
4956             cfg.cls += " " + this.cls;
4957         }
4958         return cfg;
4959     }
4960    
4961 });
4962
4963  
4964
4965  /*
4966  * - LGPL
4967  *
4968  * Pagination item
4969  * 
4970  */
4971
4972
4973 /**
4974  * @class Roo.bootstrap.PaginationItem
4975  * @extends Roo.bootstrap.Component
4976  * Bootstrap PaginationItem class
4977  * @cfg {String} html text
4978  * @cfg {String} href the link
4979  * @cfg {Boolean} preventDefault (true | false) default true
4980  * @cfg {Boolean} active (true | false) default false
4981  * @cfg {Boolean} disabled default false
4982  * 
4983  * 
4984  * @constructor
4985  * Create a new PaginationItem
4986  * @param {Object} config The config object
4987  */
4988
4989
4990 Roo.bootstrap.PaginationItem = function(config){
4991     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4992     this.addEvents({
4993         // raw events
4994         /**
4995          * @event click
4996          * The raw click event for the entire grid.
4997          * @param {Roo.EventObject} e
4998          */
4999         "click" : true
5000     });
5001 };
5002
5003 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5004     
5005     href : false,
5006     html : false,
5007     preventDefault: true,
5008     active : false,
5009     cls : false,
5010     disabled: false,
5011     
5012     getAutoCreate : function(){
5013         var cfg= {
5014             tag: 'li',
5015             cn: [
5016                 {
5017                     tag : 'a',
5018                     href : this.href ? this.href : '#',
5019                     html : this.html ? this.html : ''
5020                 }
5021             ]
5022         };
5023         
5024         if(this.cls){
5025             cfg.cls = this.cls;
5026         }
5027         
5028         if(this.disabled){
5029             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5030         }
5031         
5032         if(this.active){
5033             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5034         }
5035         
5036         return cfg;
5037     },
5038     
5039     initEvents: function() {
5040         
5041         this.el.on('click', this.onClick, this);
5042         
5043     },
5044     onClick : function(e)
5045     {
5046         Roo.log('PaginationItem on click ');
5047         if(this.preventDefault){
5048             e.preventDefault();
5049         }
5050         
5051         if(this.disabled){
5052             return;
5053         }
5054         
5055         this.fireEvent('click', this, e);
5056     }
5057    
5058 });
5059
5060  
5061
5062  /*
5063  * - LGPL
5064  *
5065  * slider
5066  * 
5067  */
5068
5069
5070 /**
5071  * @class Roo.bootstrap.Slider
5072  * @extends Roo.bootstrap.Component
5073  * Bootstrap Slider class
5074  *    
5075  * @constructor
5076  * Create a new Slider
5077  * @param {Object} config The config object
5078  */
5079
5080 Roo.bootstrap.Slider = function(config){
5081     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5082 };
5083
5084 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5085     
5086     getAutoCreate : function(){
5087         
5088         var cfg = {
5089             tag: 'div',
5090             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5091             cn: [
5092                 {
5093                     tag: 'a',
5094                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5095                 }
5096             ]
5097         };
5098         
5099         return cfg;
5100     }
5101    
5102 });
5103
5104  /*
5105  * Based on:
5106  * Ext JS Library 1.1.1
5107  * Copyright(c) 2006-2007, Ext JS, LLC.
5108  *
5109  * Originally Released Under LGPL - original licence link has changed is not relivant.
5110  *
5111  * Fork - LGPL
5112  * <script type="text/javascript">
5113  */
5114  
5115
5116 /**
5117  * @class Roo.grid.ColumnModel
5118  * @extends Roo.util.Observable
5119  * This is the default implementation of a ColumnModel used by the Grid. It defines
5120  * the columns in the grid.
5121  * <br>Usage:<br>
5122  <pre><code>
5123  var colModel = new Roo.grid.ColumnModel([
5124         {header: "Ticker", width: 60, sortable: true, locked: true},
5125         {header: "Company Name", width: 150, sortable: true},
5126         {header: "Market Cap.", width: 100, sortable: true},
5127         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5128         {header: "Employees", width: 100, sortable: true, resizable: false}
5129  ]);
5130  </code></pre>
5131  * <p>
5132  
5133  * The config options listed for this class are options which may appear in each
5134  * individual column definition.
5135  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5136  * @constructor
5137  * @param {Object} config An Array of column config objects. See this class's
5138  * config objects for details.
5139 */
5140 Roo.grid.ColumnModel = function(config){
5141         /**
5142      * The config passed into the constructor
5143      */
5144     this.config = config;
5145     this.lookup = {};
5146
5147     // if no id, create one
5148     // if the column does not have a dataIndex mapping,
5149     // map it to the order it is in the config
5150     for(var i = 0, len = config.length; i < len; i++){
5151         var c = config[i];
5152         if(typeof c.dataIndex == "undefined"){
5153             c.dataIndex = i;
5154         }
5155         if(typeof c.renderer == "string"){
5156             c.renderer = Roo.util.Format[c.renderer];
5157         }
5158         if(typeof c.id == "undefined"){
5159             c.id = Roo.id();
5160         }
5161         if(c.editor && c.editor.xtype){
5162             c.editor  = Roo.factory(c.editor, Roo.grid);
5163         }
5164         if(c.editor && c.editor.isFormField){
5165             c.editor = new Roo.grid.GridEditor(c.editor);
5166         }
5167         this.lookup[c.id] = c;
5168     }
5169
5170     /**
5171      * The width of columns which have no width specified (defaults to 100)
5172      * @type Number
5173      */
5174     this.defaultWidth = 100;
5175
5176     /**
5177      * Default sortable of columns which have no sortable specified (defaults to false)
5178      * @type Boolean
5179      */
5180     this.defaultSortable = false;
5181
5182     this.addEvents({
5183         /**
5184              * @event widthchange
5185              * Fires when the width of a column changes.
5186              * @param {ColumnModel} this
5187              * @param {Number} columnIndex The column index
5188              * @param {Number} newWidth The new width
5189              */
5190             "widthchange": true,
5191         /**
5192              * @event headerchange
5193              * Fires when the text of a header changes.
5194              * @param {ColumnModel} this
5195              * @param {Number} columnIndex The column index
5196              * @param {Number} newText The new header text
5197              */
5198             "headerchange": true,
5199         /**
5200              * @event hiddenchange
5201              * Fires when a column is hidden or "unhidden".
5202              * @param {ColumnModel} this
5203              * @param {Number} columnIndex The column index
5204              * @param {Boolean} hidden true if hidden, false otherwise
5205              */
5206             "hiddenchange": true,
5207             /**
5208          * @event columnmoved
5209          * Fires when a column is moved.
5210          * @param {ColumnModel} this
5211          * @param {Number} oldIndex
5212          * @param {Number} newIndex
5213          */
5214         "columnmoved" : true,
5215         /**
5216          * @event columlockchange
5217          * Fires when a column's locked state is changed
5218          * @param {ColumnModel} this
5219          * @param {Number} colIndex
5220          * @param {Boolean} locked true if locked
5221          */
5222         "columnlockchange" : true
5223     });
5224     Roo.grid.ColumnModel.superclass.constructor.call(this);
5225 };
5226 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5227     /**
5228      * @cfg {String} header The header text to display in the Grid view.
5229      */
5230     /**
5231      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5232      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5233      * specified, the column's index is used as an index into the Record's data Array.
5234      */
5235     /**
5236      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5237      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5238      */
5239     /**
5240      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5241      * Defaults to the value of the {@link #defaultSortable} property.
5242      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5243      */
5244     /**
5245      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5246      */
5247     /**
5248      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5249      */
5250     /**
5251      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5252      */
5253     /**
5254      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5255      */
5256     /**
5257      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5258      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5259      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5260      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5261      */
5262        /**
5263      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5264      */
5265     /**
5266      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5267      */
5268     /**
5269      * @cfg {String} cursor (Optional)
5270      */
5271     /**
5272      * @cfg {String} tooltip (Optional)
5273      */
5274     /**
5275      * @cfg {Number} xs (Optional)
5276      */
5277     /**
5278      * @cfg {Number} sm (Optional)
5279      */
5280     /**
5281      * @cfg {Number} md (Optional)
5282      */
5283     /**
5284      * @cfg {Number} lg (Optional)
5285      */
5286     /**
5287      * Returns the id of the column at the specified index.
5288      * @param {Number} index The column index
5289      * @return {String} the id
5290      */
5291     getColumnId : function(index){
5292         return this.config[index].id;
5293     },
5294
5295     /**
5296      * Returns the column for a specified id.
5297      * @param {String} id The column id
5298      * @return {Object} the column
5299      */
5300     getColumnById : function(id){
5301         return this.lookup[id];
5302     },
5303
5304     
5305     /**
5306      * Returns the column for a specified dataIndex.
5307      * @param {String} dataIndex The column dataIndex
5308      * @return {Object|Boolean} the column or false if not found
5309      */
5310     getColumnByDataIndex: function(dataIndex){
5311         var index = this.findColumnIndex(dataIndex);
5312         return index > -1 ? this.config[index] : false;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column id.
5317      * @param {String} id The column id
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     getIndexById : function(id){
5321         for(var i = 0, len = this.config.length; i < len; i++){
5322             if(this.config[i].id == id){
5323                 return i;
5324             }
5325         }
5326         return -1;
5327     },
5328     
5329     /**
5330      * Returns the index for a specified column dataIndex.
5331      * @param {String} dataIndex The column dataIndex
5332      * @return {Number} the index, or -1 if not found
5333      */
5334     
5335     findColumnIndex : function(dataIndex){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(this.config[i].dataIndex == dataIndex){
5338                 return i;
5339             }
5340         }
5341         return -1;
5342     },
5343     
5344     
5345     moveColumn : function(oldIndex, newIndex){
5346         var c = this.config[oldIndex];
5347         this.config.splice(oldIndex, 1);
5348         this.config.splice(newIndex, 0, c);
5349         this.dataMap = null;
5350         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5351     },
5352
5353     isLocked : function(colIndex){
5354         return this.config[colIndex].locked === true;
5355     },
5356
5357     setLocked : function(colIndex, value, suppressEvent){
5358         if(this.isLocked(colIndex) == value){
5359             return;
5360         }
5361         this.config[colIndex].locked = value;
5362         if(!suppressEvent){
5363             this.fireEvent("columnlockchange", this, colIndex, value);
5364         }
5365     },
5366
5367     getTotalLockedWidth : function(){
5368         var totalWidth = 0;
5369         for(var i = 0; i < this.config.length; i++){
5370             if(this.isLocked(i) && !this.isHidden(i)){
5371                 this.totalWidth += this.getColumnWidth(i);
5372             }
5373         }
5374         return totalWidth;
5375     },
5376
5377     getLockedCount : function(){
5378         for(var i = 0, len = this.config.length; i < len; i++){
5379             if(!this.isLocked(i)){
5380                 return i;
5381             }
5382         }
5383         
5384         return this.config.length;
5385     },
5386
5387     /**
5388      * Returns the number of columns.
5389      * @return {Number}
5390      */
5391     getColumnCount : function(visibleOnly){
5392         if(visibleOnly === true){
5393             var c = 0;
5394             for(var i = 0, len = this.config.length; i < len; i++){
5395                 if(!this.isHidden(i)){
5396                     c++;
5397                 }
5398             }
5399             return c;
5400         }
5401         return this.config.length;
5402     },
5403
5404     /**
5405      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5406      * @param {Function} fn
5407      * @param {Object} scope (optional)
5408      * @return {Array} result
5409      */
5410     getColumnsBy : function(fn, scope){
5411         var r = [];
5412         for(var i = 0, len = this.config.length; i < len; i++){
5413             var c = this.config[i];
5414             if(fn.call(scope||this, c, i) === true){
5415                 r[r.length] = c;
5416             }
5417         }
5418         return r;
5419     },
5420
5421     /**
5422      * Returns true if the specified column is sortable.
5423      * @param {Number} col The column index
5424      * @return {Boolean}
5425      */
5426     isSortable : function(col){
5427         if(typeof this.config[col].sortable == "undefined"){
5428             return this.defaultSortable;
5429         }
5430         return this.config[col].sortable;
5431     },
5432
5433     /**
5434      * Returns the rendering (formatting) function defined for the column.
5435      * @param {Number} col The column index.
5436      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5437      */
5438     getRenderer : function(col){
5439         if(!this.config[col].renderer){
5440             return Roo.grid.ColumnModel.defaultRenderer;
5441         }
5442         return this.config[col].renderer;
5443     },
5444
5445     /**
5446      * Sets the rendering (formatting) function for a column.
5447      * @param {Number} col The column index
5448      * @param {Function} fn The function to use to process the cell's raw data
5449      * to return HTML markup for the grid view. The render function is called with
5450      * the following parameters:<ul>
5451      * <li>Data value.</li>
5452      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5453      * <li>css A CSS style string to apply to the table cell.</li>
5454      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5455      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5456      * <li>Row index</li>
5457      * <li>Column index</li>
5458      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5459      */
5460     setRenderer : function(col, fn){
5461         this.config[col].renderer = fn;
5462     },
5463
5464     /**
5465      * Returns the width for the specified column.
5466      * @param {Number} col The column index
5467      * @return {Number}
5468      */
5469     getColumnWidth : function(col){
5470         return this.config[col].width * 1 || this.defaultWidth;
5471     },
5472
5473     /**
5474      * Sets the width for a column.
5475      * @param {Number} col The column index
5476      * @param {Number} width The new width
5477      */
5478     setColumnWidth : function(col, width, suppressEvent){
5479         this.config[col].width = width;
5480         this.totalWidth = null;
5481         if(!suppressEvent){
5482              this.fireEvent("widthchange", this, col, width);
5483         }
5484     },
5485
5486     /**
5487      * Returns the total width of all columns.
5488      * @param {Boolean} includeHidden True to include hidden column widths
5489      * @return {Number}
5490      */
5491     getTotalWidth : function(includeHidden){
5492         if(!this.totalWidth){
5493             this.totalWidth = 0;
5494             for(var i = 0, len = this.config.length; i < len; i++){
5495                 if(includeHidden || !this.isHidden(i)){
5496                     this.totalWidth += this.getColumnWidth(i);
5497                 }
5498             }
5499         }
5500         return this.totalWidth;
5501     },
5502
5503     /**
5504      * Returns the header for the specified column.
5505      * @param {Number} col The column index
5506      * @return {String}
5507      */
5508     getColumnHeader : function(col){
5509         return this.config[col].header;
5510     },
5511
5512     /**
5513      * Sets the header for a column.
5514      * @param {Number} col The column index
5515      * @param {String} header The new header
5516      */
5517     setColumnHeader : function(col, header){
5518         this.config[col].header = header;
5519         this.fireEvent("headerchange", this, col, header);
5520     },
5521
5522     /**
5523      * Returns the tooltip for the specified column.
5524      * @param {Number} col The column index
5525      * @return {String}
5526      */
5527     getColumnTooltip : function(col){
5528             return this.config[col].tooltip;
5529     },
5530     /**
5531      * Sets the tooltip for a column.
5532      * @param {Number} col The column index
5533      * @param {String} tooltip The new tooltip
5534      */
5535     setColumnTooltip : function(col, tooltip){
5536             this.config[col].tooltip = tooltip;
5537     },
5538
5539     /**
5540      * Returns the dataIndex for the specified column.
5541      * @param {Number} col The column index
5542      * @return {Number}
5543      */
5544     getDataIndex : function(col){
5545         return this.config[col].dataIndex;
5546     },
5547
5548     /**
5549      * Sets the dataIndex for a column.
5550      * @param {Number} col The column index
5551      * @param {Number} dataIndex The new dataIndex
5552      */
5553     setDataIndex : function(col, dataIndex){
5554         this.config[col].dataIndex = dataIndex;
5555     },
5556
5557     
5558     
5559     /**
5560      * Returns true if the cell is editable.
5561      * @param {Number} colIndex The column index
5562      * @param {Number} rowIndex The row index - this is nto actually used..?
5563      * @return {Boolean}
5564      */
5565     isCellEditable : function(colIndex, rowIndex){
5566         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5567     },
5568
5569     /**
5570      * Returns the editor defined for the cell/column.
5571      * return false or null to disable editing.
5572      * @param {Number} colIndex The column index
5573      * @param {Number} rowIndex The row index
5574      * @return {Object}
5575      */
5576     getCellEditor : function(colIndex, rowIndex){
5577         return this.config[colIndex].editor;
5578     },
5579
5580     /**
5581      * Sets if a column is editable.
5582      * @param {Number} col The column index
5583      * @param {Boolean} editable True if the column is editable
5584      */
5585     setEditable : function(col, editable){
5586         this.config[col].editable = editable;
5587     },
5588
5589
5590     /**
5591      * Returns true if the column is hidden.
5592      * @param {Number} colIndex The column index
5593      * @return {Boolean}
5594      */
5595     isHidden : function(colIndex){
5596         return this.config[colIndex].hidden;
5597     },
5598
5599
5600     /**
5601      * Returns true if the column width cannot be changed
5602      */
5603     isFixed : function(colIndex){
5604         return this.config[colIndex].fixed;
5605     },
5606
5607     /**
5608      * Returns true if the column can be resized
5609      * @return {Boolean}
5610      */
5611     isResizable : function(colIndex){
5612         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5613     },
5614     /**
5615      * Sets if a column is hidden.
5616      * @param {Number} colIndex The column index
5617      * @param {Boolean} hidden True if the column is hidden
5618      */
5619     setHidden : function(colIndex, hidden){
5620         this.config[colIndex].hidden = hidden;
5621         this.totalWidth = null;
5622         this.fireEvent("hiddenchange", this, colIndex, hidden);
5623     },
5624
5625     /**
5626      * Sets the editor for a column.
5627      * @param {Number} col The column index
5628      * @param {Object} editor The editor object
5629      */
5630     setEditor : function(col, editor){
5631         this.config[col].editor = editor;
5632     }
5633 });
5634
5635 Roo.grid.ColumnModel.defaultRenderer = function(value)
5636 {
5637     if(typeof value == "object") {
5638         return value;
5639     }
5640         if(typeof value == "string" && value.length < 1){
5641             return "&#160;";
5642         }
5643     
5644         return String.format("{0}", value);
5645 };
5646
5647 // Alias for backwards compatibility
5648 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5649 /*
5650  * Based on:
5651  * Ext JS Library 1.1.1
5652  * Copyright(c) 2006-2007, Ext JS, LLC.
5653  *
5654  * Originally Released Under LGPL - original licence link has changed is not relivant.
5655  *
5656  * Fork - LGPL
5657  * <script type="text/javascript">
5658  */
5659  
5660 /**
5661  * @class Roo.LoadMask
5662  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5663  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5664  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5665  * element's UpdateManager load indicator and will be destroyed after the initial load.
5666  * @constructor
5667  * Create a new LoadMask
5668  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5669  * @param {Object} config The config object
5670  */
5671 Roo.LoadMask = function(el, config){
5672     this.el = Roo.get(el);
5673     Roo.apply(this, config);
5674     if(this.store){
5675         this.store.on('beforeload', this.onBeforeLoad, this);
5676         this.store.on('load', this.onLoad, this);
5677         this.store.on('loadexception', this.onLoadException, this);
5678         this.removeMask = false;
5679     }else{
5680         var um = this.el.getUpdateManager();
5681         um.showLoadIndicator = false; // disable the default indicator
5682         um.on('beforeupdate', this.onBeforeLoad, this);
5683         um.on('update', this.onLoad, this);
5684         um.on('failure', this.onLoad, this);
5685         this.removeMask = true;
5686     }
5687 };
5688
5689 Roo.LoadMask.prototype = {
5690     /**
5691      * @cfg {Boolean} removeMask
5692      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5693      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5694      */
5695     /**
5696      * @cfg {String} msg
5697      * The text to display in a centered loading message box (defaults to 'Loading...')
5698      */
5699     msg : 'Loading...',
5700     /**
5701      * @cfg {String} msgCls
5702      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5703      */
5704     msgCls : 'x-mask-loading',
5705
5706     /**
5707      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5708      * @type Boolean
5709      */
5710     disabled: false,
5711
5712     /**
5713      * Disables the mask to prevent it from being displayed
5714      */
5715     disable : function(){
5716        this.disabled = true;
5717     },
5718
5719     /**
5720      * Enables the mask so that it can be displayed
5721      */
5722     enable : function(){
5723         this.disabled = false;
5724     },
5725     
5726     onLoadException : function()
5727     {
5728         Roo.log(arguments);
5729         
5730         if (typeof(arguments[3]) != 'undefined') {
5731             Roo.MessageBox.alert("Error loading",arguments[3]);
5732         } 
5733         /*
5734         try {
5735             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5736                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5737             }   
5738         } catch(e) {
5739             
5740         }
5741         */
5742     
5743         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5744     },
5745     // private
5746     onLoad : function()
5747     {
5748         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5749     },
5750
5751     // private
5752     onBeforeLoad : function(){
5753         if(!this.disabled){
5754             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5755         }
5756     },
5757
5758     // private
5759     destroy : function(){
5760         if(this.store){
5761             this.store.un('beforeload', this.onBeforeLoad, this);
5762             this.store.un('load', this.onLoad, this);
5763             this.store.un('loadexception', this.onLoadException, this);
5764         }else{
5765             var um = this.el.getUpdateManager();
5766             um.un('beforeupdate', this.onBeforeLoad, this);
5767             um.un('update', this.onLoad, this);
5768             um.un('failure', this.onLoad, this);
5769         }
5770     }
5771 };/*
5772  * - LGPL
5773  *
5774  * table
5775  * 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.Table
5780  * @extends Roo.bootstrap.Component
5781  * Bootstrap Table class
5782  * @cfg {String} cls table class
5783  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5784  * @cfg {String} bgcolor Specifies the background color for a table
5785  * @cfg {Number} border Specifies whether the table cells should have borders or not
5786  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5787  * @cfg {Number} cellspacing Specifies the space between cells
5788  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5789  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5790  * @cfg {String} sortable Specifies that the table should be sortable
5791  * @cfg {String} summary Specifies a summary of the content of a table
5792  * @cfg {Number} width Specifies the width of a table
5793  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5794  * 
5795  * @cfg {boolean} striped Should the rows be alternative striped
5796  * @cfg {boolean} bordered Add borders to the table
5797  * @cfg {boolean} hover Add hover highlighting
5798  * @cfg {boolean} condensed Format condensed
5799  * @cfg {boolean} responsive Format condensed
5800  * @cfg {Boolean} loadMask (true|false) default false
5801  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5802  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5803  * @cfg {Boolean} rowSelection (true|false) default false
5804  * @cfg {Boolean} cellSelection (true|false) default false
5805  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5806  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5807  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5808  
5809  * 
5810  * @constructor
5811  * Create a new Table
5812  * @param {Object} config The config object
5813  */
5814
5815 Roo.bootstrap.Table = function(config){
5816     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5817     
5818   
5819     
5820     // BC...
5821     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5822     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5823     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5824     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5825     
5826     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5827     if (this.sm) {
5828         this.sm.grid = this;
5829         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5830         this.sm = this.selModel;
5831         this.sm.xmodule = this.xmodule || false;
5832     }
5833     
5834     if (this.cm && typeof(this.cm.config) == 'undefined') {
5835         this.colModel = new Roo.grid.ColumnModel(this.cm);
5836         this.cm = this.colModel;
5837         this.cm.xmodule = this.xmodule || false;
5838     }
5839     if (this.store) {
5840         this.store= Roo.factory(this.store, Roo.data);
5841         this.ds = this.store;
5842         this.ds.xmodule = this.xmodule || false;
5843          
5844     }
5845     if (this.footer && this.store) {
5846         this.footer.dataSource = this.ds;
5847         this.footer = Roo.factory(this.footer);
5848     }
5849     
5850     /** @private */
5851     this.addEvents({
5852         /**
5853          * @event cellclick
5854          * Fires when a cell is clicked
5855          * @param {Roo.bootstrap.Table} this
5856          * @param {Roo.Element} el
5857          * @param {Number} rowIndex
5858          * @param {Number} columnIndex
5859          * @param {Roo.EventObject} e
5860          */
5861         "cellclick" : true,
5862         /**
5863          * @event celldblclick
5864          * Fires when a cell is double clicked
5865          * @param {Roo.bootstrap.Table} this
5866          * @param {Roo.Element} el
5867          * @param {Number} rowIndex
5868          * @param {Number} columnIndex
5869          * @param {Roo.EventObject} e
5870          */
5871         "celldblclick" : true,
5872         /**
5873          * @event rowclick
5874          * Fires when a row is clicked
5875          * @param {Roo.bootstrap.Table} this
5876          * @param {Roo.Element} el
5877          * @param {Number} rowIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "rowclick" : true,
5881         /**
5882          * @event rowdblclick
5883          * Fires when a row is double clicked
5884          * @param {Roo.bootstrap.Table} this
5885          * @param {Roo.Element} el
5886          * @param {Number} rowIndex
5887          * @param {Roo.EventObject} e
5888          */
5889         "rowdblclick" : true,
5890         /**
5891          * @event mouseover
5892          * Fires when a mouseover occur
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Roo.Element} el
5895          * @param {Number} rowIndex
5896          * @param {Number} columnIndex
5897          * @param {Roo.EventObject} e
5898          */
5899         "mouseover" : true,
5900         /**
5901          * @event mouseout
5902          * Fires when a mouseout occur
5903          * @param {Roo.bootstrap.Table} this
5904          * @param {Roo.Element} el
5905          * @param {Number} rowIndex
5906          * @param {Number} columnIndex
5907          * @param {Roo.EventObject} e
5908          */
5909         "mouseout" : true,
5910         /**
5911          * @event rowclass
5912          * Fires when a row is rendered, so you can change add a style to it.
5913          * @param {Roo.bootstrap.Table} this
5914          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5915          */
5916         'rowclass' : true,
5917           /**
5918          * @event rowsrendered
5919          * Fires when all the  rows have been rendered
5920          * @param {Roo.bootstrap.Table} this
5921          */
5922         'rowsrendered' : true,
5923         /**
5924          * @event contextmenu
5925          * The raw contextmenu event for the entire grid.
5926          * @param {Roo.EventObject} e
5927          */
5928         "contextmenu" : true,
5929         /**
5930          * @event rowcontextmenu
5931          * Fires when a row is right clicked
5932          * @param {Roo.bootstrap.Table} this
5933          * @param {Number} rowIndex
5934          * @param {Roo.EventObject} e
5935          */
5936         "rowcontextmenu" : true,
5937         /**
5938          * @event cellcontextmenu
5939          * Fires when a cell is right clicked
5940          * @param {Roo.bootstrap.Table} this
5941          * @param {Number} rowIndex
5942          * @param {Number} cellIndex
5943          * @param {Roo.EventObject} e
5944          */
5945          "cellcontextmenu" : true,
5946          /**
5947          * @event headercontextmenu
5948          * Fires when a header is right clicked
5949          * @param {Roo.bootstrap.Table} this
5950          * @param {Number} columnIndex
5951          * @param {Roo.EventObject} e
5952          */
5953         "headercontextmenu" : true
5954     });
5955 };
5956
5957 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5958     
5959     cls: false,
5960     align: false,
5961     bgcolor: false,
5962     border: false,
5963     cellpadding: false,
5964     cellspacing: false,
5965     frame: false,
5966     rules: false,
5967     sortable: false,
5968     summary: false,
5969     width: false,
5970     striped : false,
5971     scrollBody : false,
5972     bordered: false,
5973     hover:  false,
5974     condensed : false,
5975     responsive : false,
5976     sm : false,
5977     cm : false,
5978     store : false,
5979     loadMask : false,
5980     footerShow : true,
5981     headerShow : true,
5982   
5983     rowSelection : false,
5984     cellSelection : false,
5985     layout : false,
5986     
5987     // Roo.Element - the tbody
5988     mainBody: false,
5989     // Roo.Element - thead element
5990     mainHead: false,
5991     
5992     container: false, // used by gridpanel...
5993     
5994     lazyLoad : false,
5995     
5996     getAutoCreate : function()
5997     {
5998         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5999         
6000         cfg = {
6001             tag: 'table',
6002             cls : 'table',
6003             cn : []
6004         };
6005         if (this.scrollBody) {
6006             cfg.cls += ' table-body-fixed';
6007         }    
6008         if (this.striped) {
6009             cfg.cls += ' table-striped';
6010         }
6011         
6012         if (this.hover) {
6013             cfg.cls += ' table-hover';
6014         }
6015         if (this.bordered) {
6016             cfg.cls += ' table-bordered';
6017         }
6018         if (this.condensed) {
6019             cfg.cls += ' table-condensed';
6020         }
6021         if (this.responsive) {
6022             cfg.cls += ' table-responsive';
6023         }
6024         
6025         if (this.cls) {
6026             cfg.cls+=  ' ' +this.cls;
6027         }
6028         
6029         // this lot should be simplifed...
6030         
6031         if (this.align) {
6032             cfg.align=this.align;
6033         }
6034         if (this.bgcolor) {
6035             cfg.bgcolor=this.bgcolor;
6036         }
6037         if (this.border) {
6038             cfg.border=this.border;
6039         }
6040         if (this.cellpadding) {
6041             cfg.cellpadding=this.cellpadding;
6042         }
6043         if (this.cellspacing) {
6044             cfg.cellspacing=this.cellspacing;
6045         }
6046         if (this.frame) {
6047             cfg.frame=this.frame;
6048         }
6049         if (this.rules) {
6050             cfg.rules=this.rules;
6051         }
6052         if (this.sortable) {
6053             cfg.sortable=this.sortable;
6054         }
6055         if (this.summary) {
6056             cfg.summary=this.summary;
6057         }
6058         if (this.width) {
6059             cfg.width=this.width;
6060         }
6061         if (this.layout) {
6062             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6063         }
6064         
6065         if(this.store || this.cm){
6066             if(this.headerShow){
6067                 cfg.cn.push(this.renderHeader());
6068             }
6069             
6070             cfg.cn.push(this.renderBody());
6071             
6072             if(this.footerShow){
6073                 cfg.cn.push(this.renderFooter());
6074             }
6075             // where does this come from?
6076             //cfg.cls+=  ' TableGrid';
6077         }
6078         
6079         return { cn : [ cfg ] };
6080     },
6081     
6082     initEvents : function()
6083     {   
6084         if(!this.store || !this.cm){
6085             return;
6086         }
6087         if (this.selModel) {
6088             this.selModel.initEvents();
6089         }
6090         
6091         
6092         //Roo.log('initEvents with ds!!!!');
6093         
6094         this.mainBody = this.el.select('tbody', true).first();
6095         this.mainHead = this.el.select('thead', true).first();
6096         
6097         
6098         
6099         
6100         var _this = this;
6101         
6102         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6103             e.on('click', _this.sort, _this);
6104         });
6105         
6106         this.mainBody.on("click", this.onClick, this);
6107         this.mainBody.on("dblclick", this.onDblClick, this);
6108         
6109         // why is this done????? = it breaks dialogs??
6110         //this.parent().el.setStyle('position', 'relative');
6111         
6112         
6113         if (this.footer) {
6114             this.footer.parentId = this.id;
6115             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6116             
6117             if(this.lazyLoad){
6118                 this.el.select('tfoot tr td').first().addClass('hide');
6119             }
6120         } 
6121         
6122         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6123         
6124         this.store.on('load', this.onLoad, this);
6125         this.store.on('beforeload', this.onBeforeLoad, this);
6126         this.store.on('update', this.onUpdate, this);
6127         this.store.on('add', this.onAdd, this);
6128         this.store.on("clear", this.clear, this);
6129         
6130         this.el.on("contextmenu", this.onContextMenu, this);
6131         
6132         this.mainBody.on('scroll', this.onBodyScroll, this);
6133         
6134         this.cm.on("headerchange", this.onHeaderChange, this);
6135         
6136     },
6137     
6138     onContextMenu : function(e, t)
6139     {
6140         this.processEvent("contextmenu", e);
6141     },
6142     
6143     processEvent : function(name, e)
6144     {
6145         if (name != 'touchstart' ) {
6146             this.fireEvent(name, e);    
6147         }
6148         
6149         var t = e.getTarget();
6150         
6151         var cell = Roo.get(t);
6152         
6153         if(!cell){
6154             return;
6155         }
6156         
6157         if(cell.findParent('tfoot', false, true)){
6158             return;
6159         }
6160         
6161         if(cell.findParent('thead', false, true)){
6162             
6163             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6164                 cell = Roo.get(t).findParent('th', false, true);
6165                 if (!cell) {
6166                     Roo.log("failed to find th in thead?");
6167                     Roo.log(e.getTarget());
6168                     return;
6169                 }
6170             }
6171             
6172             var cellIndex = cell.dom.cellIndex;
6173             
6174             var ename = name == 'touchstart' ? 'click' : name;
6175             this.fireEvent("header" + ename, this, cellIndex, e);
6176             
6177             return;
6178         }
6179         
6180         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6181             cell = Roo.get(t).findParent('td', false, true);
6182             if (!cell) {
6183                 Roo.log("failed to find th in tbody?");
6184                 Roo.log(e.getTarget());
6185                 return;
6186             }
6187         }
6188         
6189         var row = cell.findParent('tr', false, true);
6190         var cellIndex = cell.dom.cellIndex;
6191         var rowIndex = row.dom.rowIndex - 1;
6192         
6193         if(row !== false){
6194             
6195             this.fireEvent("row" + name, this, rowIndex, e);
6196             
6197             if(cell !== false){
6198             
6199                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6200             }
6201         }
6202         
6203     },
6204     
6205     onMouseover : function(e, el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         var row = cell.findParent('tr', false, true);
6218         var cellIndex = cell.dom.cellIndex;
6219         var rowIndex = row.dom.rowIndex - 1; // start from 0
6220         
6221         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6222         
6223     },
6224     
6225     onMouseout : function(e, el)
6226     {
6227         var cell = Roo.get(el);
6228         
6229         if(!cell){
6230             return;
6231         }
6232         
6233         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6234             cell = cell.findParent('td', false, true);
6235         }
6236         
6237         var row = cell.findParent('tr', false, true);
6238         var cellIndex = cell.dom.cellIndex;
6239         var rowIndex = row.dom.rowIndex - 1; // start from 0
6240         
6241         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6242         
6243     },
6244     
6245     onClick : function(e, el)
6246     {
6247         var cell = Roo.get(el);
6248         
6249         if(!cell || (!this.cellSelection && !this.rowSelection)){
6250             return;
6251         }
6252         
6253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254             cell = cell.findParent('td', false, true);
6255         }
6256         
6257         if(!cell || typeof(cell) == 'undefined'){
6258             return;
6259         }
6260         
6261         var row = cell.findParent('tr', false, true);
6262         
6263         if(!row || typeof(row) == 'undefined'){
6264             return;
6265         }
6266         
6267         var cellIndex = cell.dom.cellIndex;
6268         var rowIndex = this.getRowIndex(row);
6269         
6270         // why??? - should these not be based on SelectionModel?
6271         if(this.cellSelection){
6272             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6273         }
6274         
6275         if(this.rowSelection){
6276             this.fireEvent('rowclick', this, row, rowIndex, e);
6277         }
6278         
6279         
6280     },
6281         
6282     onDblClick : function(e,el)
6283     {
6284         var cell = Roo.get(el);
6285         
6286         if(!cell || (!this.cellSelection && !this.rowSelection)){
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = cell.findParent('td', false, true);
6292         }
6293         
6294         if(!cell || typeof(cell) == 'undefined'){
6295             return;
6296         }
6297         
6298         var row = cell.findParent('tr', false, true);
6299         
6300         if(!row || typeof(row) == 'undefined'){
6301             return;
6302         }
6303         
6304         var cellIndex = cell.dom.cellIndex;
6305         var rowIndex = this.getRowIndex(row);
6306         
6307         if(this.cellSelection){
6308             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6309         }
6310         
6311         if(this.rowSelection){
6312             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6313         }
6314     },
6315     
6316     sort : function(e,el)
6317     {
6318         var col = Roo.get(el);
6319         
6320         if(!col.hasClass('sortable')){
6321             return;
6322         }
6323         
6324         var sort = col.attr('sort');
6325         var dir = 'ASC';
6326         
6327         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6328             dir = 'DESC';
6329         }
6330         
6331         this.store.sortInfo = {field : sort, direction : dir};
6332         
6333         if (this.footer) {
6334             Roo.log("calling footer first");
6335             this.footer.onClick('first');
6336         } else {
6337         
6338             this.store.load({ params : { start : 0 } });
6339         }
6340     },
6341     
6342     renderHeader : function()
6343     {
6344         var header = {
6345             tag: 'thead',
6346             cn : []
6347         };
6348         
6349         var cm = this.cm;
6350         this.totalWidth = 0;
6351         
6352         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6353             
6354             var config = cm.config[i];
6355             
6356             var c = {
6357                 tag: 'th',
6358                 style : '',
6359                 html: cm.getColumnHeader(i)
6360             };
6361             
6362             var hh = '';
6363             
6364             if(typeof(config.sortable) != 'undefined' && config.sortable){
6365                 c.cls = 'sortable';
6366                 c.html = '<i class="glyphicon"></i>' + c.html;
6367             }
6368             
6369             if(typeof(config.lgHeader) != 'undefined'){
6370                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6371             }
6372             
6373             if(typeof(config.mdHeader) != 'undefined'){
6374                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6375             }
6376             
6377             if(typeof(config.smHeader) != 'undefined'){
6378                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6379             }
6380             
6381             if(typeof(config.xsHeader) != 'undefined'){
6382                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6383             }
6384             
6385             if(hh.length){
6386                 c.html = hh;
6387             }
6388             
6389             if(typeof(config.tooltip) != 'undefined'){
6390                 c.tooltip = config.tooltip;
6391             }
6392             
6393             if(typeof(config.colspan) != 'undefined'){
6394                 c.colspan = config.colspan;
6395             }
6396             
6397             if(typeof(config.hidden) != 'undefined' && config.hidden){
6398                 c.style += ' display:none;';
6399             }
6400             
6401             if(typeof(config.dataIndex) != 'undefined'){
6402                 c.sort = config.dataIndex;
6403             }
6404             
6405            
6406             
6407             if(typeof(config.align) != 'undefined' && config.align.length){
6408                 c.style += ' text-align:' + config.align + ';';
6409             }
6410             
6411             if(typeof(config.width) != 'undefined'){
6412                 c.style += ' width:' + config.width + 'px;';
6413                 this.totalWidth += config.width;
6414             } else {
6415                 this.totalWidth += 100; // assume minimum of 100 per column?
6416             }
6417             
6418             if(typeof(config.cls) != 'undefined'){
6419                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6420             }
6421             
6422             ['xs','sm','md','lg'].map(function(size){
6423                 
6424                 if(typeof(config[size]) == 'undefined'){
6425                     return;
6426                 }
6427                 
6428                 if (!config[size]) { // 0 = hidden
6429                     c.cls += ' hidden-' + size;
6430                     return;
6431                 }
6432                 
6433                 c.cls += ' col-' + size + '-' + config[size];
6434
6435             });
6436             
6437             header.cn.push(c)
6438         }
6439         
6440         return header;
6441     },
6442     
6443     renderBody : function()
6444     {
6445         var body = {
6446             tag: 'tbody',
6447             cn : [
6448                 {
6449                     tag: 'tr',
6450                     cn : [
6451                         {
6452                             tag : 'td',
6453                             colspan :  this.cm.getColumnCount()
6454                         }
6455                     ]
6456                 }
6457             ]
6458         };
6459         
6460         return body;
6461     },
6462     
6463     renderFooter : function()
6464     {
6465         var footer = {
6466             tag: 'tfoot',
6467             cn : [
6468                 {
6469                     tag: 'tr',
6470                     cn : [
6471                         {
6472                             tag : 'td',
6473                             colspan :  this.cm.getColumnCount()
6474                         }
6475                     ]
6476                 }
6477             ]
6478         };
6479         
6480         return footer;
6481     },
6482     
6483     
6484     
6485     onLoad : function()
6486     {
6487 //        Roo.log('ds onload');
6488         this.clear();
6489         
6490         var _this = this;
6491         var cm = this.cm;
6492         var ds = this.store;
6493         
6494         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6495             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6496             if (_this.store.sortInfo) {
6497                     
6498                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6499                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6500                 }
6501                 
6502                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6503                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6504                 }
6505             }
6506         });
6507         
6508         var tbody =  this.mainBody;
6509               
6510         if(ds.getCount() > 0){
6511             ds.data.each(function(d,rowIndex){
6512                 var row =  this.renderRow(cm, ds, rowIndex);
6513                 
6514                 tbody.createChild(row);
6515                 
6516                 var _this = this;
6517                 
6518                 if(row.cellObjects.length){
6519                     Roo.each(row.cellObjects, function(r){
6520                         _this.renderCellObject(r);
6521                     })
6522                 }
6523                 
6524             }, this);
6525         }
6526         
6527         Roo.each(this.el.select('tbody td', true).elements, function(e){
6528             e.on('mouseover', _this.onMouseover, _this);
6529         });
6530         
6531         Roo.each(this.el.select('tbody td', true).elements, function(e){
6532             e.on('mouseout', _this.onMouseout, _this);
6533         });
6534         this.fireEvent('rowsrendered', this);
6535         //if(this.loadMask){
6536         //    this.maskEl.hide();
6537         //}
6538         
6539         this.autoSize();
6540     },
6541     
6542     
6543     onUpdate : function(ds,record)
6544     {
6545         this.refreshRow(record);
6546         this.autoSize();
6547     },
6548     
6549     onRemove : function(ds, record, index, isUpdate){
6550         if(isUpdate !== true){
6551             this.fireEvent("beforerowremoved", this, index, record);
6552         }
6553         var bt = this.mainBody.dom;
6554         
6555         var rows = this.el.select('tbody > tr', true).elements;
6556         
6557         if(typeof(rows[index]) != 'undefined'){
6558             bt.removeChild(rows[index].dom);
6559         }
6560         
6561 //        if(bt.rows[index]){
6562 //            bt.removeChild(bt.rows[index]);
6563 //        }
6564         
6565         if(isUpdate !== true){
6566             //this.stripeRows(index);
6567             //this.syncRowHeights(index, index);
6568             //this.layout();
6569             this.fireEvent("rowremoved", this, index, record);
6570         }
6571     },
6572     
6573     onAdd : function(ds, records, rowIndex)
6574     {
6575         //Roo.log('on Add called');
6576         // - note this does not handle multiple adding very well..
6577         var bt = this.mainBody.dom;
6578         for (var i =0 ; i < records.length;i++) {
6579             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6580             //Roo.log(records[i]);
6581             //Roo.log(this.store.getAt(rowIndex+i));
6582             this.insertRow(this.store, rowIndex + i, false);
6583             return;
6584         }
6585         
6586     },
6587     
6588     
6589     refreshRow : function(record){
6590         var ds = this.store, index;
6591         if(typeof record == 'number'){
6592             index = record;
6593             record = ds.getAt(index);
6594         }else{
6595             index = ds.indexOf(record);
6596         }
6597         this.insertRow(ds, index, true);
6598         this.autoSize();
6599         this.onRemove(ds, record, index+1, true);
6600         this.autoSize();
6601         //this.syncRowHeights(index, index);
6602         //this.layout();
6603         this.fireEvent("rowupdated", this, index, record);
6604     },
6605     
6606     insertRow : function(dm, rowIndex, isUpdate){
6607         
6608         if(!isUpdate){
6609             this.fireEvent("beforerowsinserted", this, rowIndex);
6610         }
6611             //var s = this.getScrollState();
6612         var row = this.renderRow(this.cm, this.store, rowIndex);
6613         // insert before rowIndex..
6614         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6615         
6616         var _this = this;
6617                 
6618         if(row.cellObjects.length){
6619             Roo.each(row.cellObjects, function(r){
6620                 _this.renderCellObject(r);
6621             })
6622         }
6623             
6624         if(!isUpdate){
6625             this.fireEvent("rowsinserted", this, rowIndex);
6626             //this.syncRowHeights(firstRow, lastRow);
6627             //this.stripeRows(firstRow);
6628             //this.layout();
6629         }
6630         
6631     },
6632     
6633     
6634     getRowDom : function(rowIndex)
6635     {
6636         var rows = this.el.select('tbody > tr', true).elements;
6637         
6638         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6639         
6640     },
6641     // returns the object tree for a tr..
6642   
6643     
6644     renderRow : function(cm, ds, rowIndex) 
6645     {
6646         
6647         var d = ds.getAt(rowIndex);
6648         
6649         var row = {
6650             tag : 'tr',
6651             cn : []
6652         };
6653             
6654         var cellObjects = [];
6655         
6656         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6657             var config = cm.config[i];
6658             
6659             var renderer = cm.getRenderer(i);
6660             var value = '';
6661             var id = false;
6662             
6663             if(typeof(renderer) !== 'undefined'){
6664                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6665             }
6666             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6667             // and are rendered into the cells after the row is rendered - using the id for the element.
6668             
6669             if(typeof(value) === 'object'){
6670                 id = Roo.id();
6671                 cellObjects.push({
6672                     container : id,
6673                     cfg : value 
6674                 })
6675             }
6676             
6677             var rowcfg = {
6678                 record: d,
6679                 rowIndex : rowIndex,
6680                 colIndex : i,
6681                 rowClass : ''
6682             };
6683
6684             this.fireEvent('rowclass', this, rowcfg);
6685             
6686             var td = {
6687                 tag: 'td',
6688                 cls : rowcfg.rowClass,
6689                 style: '',
6690                 html: (typeof(value) === 'object') ? '' : value
6691             };
6692             
6693             if (id) {
6694                 td.id = id;
6695             }
6696             
6697             if(typeof(config.colspan) != 'undefined'){
6698                 td.colspan = config.colspan;
6699             }
6700             
6701             if(typeof(config.hidden) != 'undefined' && config.hidden){
6702                 td.style += ' display:none;';
6703             }
6704             
6705             if(typeof(config.align) != 'undefined' && config.align.length){
6706                 td.style += ' text-align:' + config.align + ';';
6707             }
6708             
6709             if(typeof(config.width) != 'undefined'){
6710                 td.style += ' width:' +  config.width + 'px;';
6711             }
6712             
6713             if(typeof(config.cursor) != 'undefined'){
6714                 td.style += ' cursor:' +  config.cursor + ';';
6715             }
6716             
6717             if(typeof(config.cls) != 'undefined'){
6718                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6719             }
6720             
6721             ['xs','sm','md','lg'].map(function(size){
6722                 
6723                 if(typeof(config[size]) == 'undefined'){
6724                     return;
6725                 }
6726                 
6727                 if (!config[size]) { // 0 = hidden
6728                     td.cls += ' hidden-' + size;
6729                     return;
6730                 }
6731                 
6732                 td.cls += ' col-' + size + '-' + config[size];
6733
6734             });
6735              
6736             row.cn.push(td);
6737            
6738         }
6739         
6740         row.cellObjects = cellObjects;
6741         
6742         return row;
6743           
6744     },
6745     
6746     
6747     
6748     onBeforeLoad : function()
6749     {
6750         //Roo.log('ds onBeforeLoad');
6751         
6752         //this.clear();
6753         
6754         //if(this.loadMask){
6755         //    this.maskEl.show();
6756         //}
6757     },
6758      /**
6759      * Remove all rows
6760      */
6761     clear : function()
6762     {
6763         this.el.select('tbody', true).first().dom.innerHTML = '';
6764     },
6765     /**
6766      * Show or hide a row.
6767      * @param {Number} rowIndex to show or hide
6768      * @param {Boolean} state hide
6769      */
6770     setRowVisibility : function(rowIndex, state)
6771     {
6772         var bt = this.mainBody.dom;
6773         
6774         var rows = this.el.select('tbody > tr', true).elements;
6775         
6776         if(typeof(rows[rowIndex]) == 'undefined'){
6777             return;
6778         }
6779         rows[rowIndex].dom.style.display = state ? '' : 'none';
6780     },
6781     
6782     
6783     getSelectionModel : function(){
6784         if(!this.selModel){
6785             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6786         }
6787         return this.selModel;
6788     },
6789     /*
6790      * Render the Roo.bootstrap object from renderder
6791      */
6792     renderCellObject : function(r)
6793     {
6794         var _this = this;
6795         
6796         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6797         
6798         var t = r.cfg.render(r.container);
6799         
6800         if(r.cfg.cn){
6801             Roo.each(r.cfg.cn, function(c){
6802                 var child = {
6803                     container: t.getChildContainer(),
6804                     cfg: c
6805                 };
6806                 _this.renderCellObject(child);
6807             })
6808         }
6809     },
6810     
6811     getRowIndex : function(row)
6812     {
6813         var rowIndex = -1;
6814         
6815         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6816             if(el != row){
6817                 return;
6818             }
6819             
6820             rowIndex = index;
6821         });
6822         
6823         return rowIndex;
6824     },
6825      /**
6826      * Returns the grid's underlying element = used by panel.Grid
6827      * @return {Element} The element
6828      */
6829     getGridEl : function(){
6830         return this.el;
6831     },
6832      /**
6833      * Forces a resize - used by panel.Grid
6834      * @return {Element} The element
6835      */
6836     autoSize : function()
6837     {
6838         //var ctr = Roo.get(this.container.dom.parentElement);
6839         var ctr = Roo.get(this.el.dom);
6840         
6841         var thd = this.getGridEl().select('thead',true).first();
6842         var tbd = this.getGridEl().select('tbody', true).first();
6843         var tfd = this.getGridEl().select('tfoot', true).first();
6844         
6845         var cw = ctr.getWidth();
6846         
6847         if (tbd) {
6848             
6849             tbd.setSize(ctr.getWidth(),
6850                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6851             );
6852             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6853             cw -= barsize;
6854         }
6855         cw = Math.max(cw, this.totalWidth);
6856         this.getGridEl().select('tr',true).setWidth(cw);
6857         // resize 'expandable coloumn?
6858         
6859         return; // we doe not have a view in this design..
6860         
6861     },
6862     onBodyScroll: function()
6863     {
6864         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6865         if(this.mainHead){
6866             this.mainHead.setStyle({
6867                 'position' : 'relative',
6868                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6869             });
6870         }
6871         
6872         if(this.lazyLoad){
6873             
6874             var scrollHeight = this.mainBody.dom.scrollHeight;
6875             
6876             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6877             
6878             var height = this.mainBody.getHeight();
6879             
6880             if(scrollHeight - height == scrollTop) {
6881                 
6882                 var total = this.ds.getTotalCount();
6883                 
6884                 if(this.footer.cursor + this.footer.pageSize < total){
6885                     
6886                     this.footer.ds.load({
6887                         params : {
6888                             start : this.footer.cursor + this.footer.pageSize,
6889                             limit : this.footer.pageSize
6890                         },
6891                         add : true
6892                     });
6893                 }
6894             }
6895             
6896         }
6897     },
6898     
6899     onHeaderChange : function()
6900     {
6901         
6902         var header = this.renderHeader();
6903         var table = this.el.select('table', true).first();
6904         
6905         this.mainHead.remove();
6906         this.mainHead = table.createChild(header, this.mainBody, false);
6907     }
6908     
6909 });
6910
6911  
6912
6913  /*
6914  * - LGPL
6915  *
6916  * table cell
6917  * 
6918  */
6919
6920 /**
6921  * @class Roo.bootstrap.TableCell
6922  * @extends Roo.bootstrap.Component
6923  * Bootstrap TableCell class
6924  * @cfg {String} html cell contain text
6925  * @cfg {String} cls cell class
6926  * @cfg {String} tag cell tag (td|th) default td
6927  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6928  * @cfg {String} align Aligns the content in a cell
6929  * @cfg {String} axis Categorizes cells
6930  * @cfg {String} bgcolor Specifies the background color of a cell
6931  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6932  * @cfg {Number} colspan Specifies the number of columns a cell should span
6933  * @cfg {String} headers Specifies one or more header cells a cell is related to
6934  * @cfg {Number} height Sets the height of a cell
6935  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6936  * @cfg {Number} rowspan Sets the number of rows a cell should span
6937  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6938  * @cfg {String} valign Vertical aligns the content in a cell
6939  * @cfg {Number} width Specifies the width of a cell
6940  * 
6941  * @constructor
6942  * Create a new TableCell
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.TableCell = function(config){
6947     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6948 };
6949
6950 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6951     
6952     html: false,
6953     cls: false,
6954     tag: false,
6955     abbr: false,
6956     align: false,
6957     axis: false,
6958     bgcolor: false,
6959     charoff: false,
6960     colspan: false,
6961     headers: false,
6962     height: false,
6963     nowrap: false,
6964     rowspan: false,
6965     scope: false,
6966     valign: false,
6967     width: false,
6968     
6969     
6970     getAutoCreate : function(){
6971         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6972         
6973         cfg = {
6974             tag: 'td'
6975         };
6976         
6977         if(this.tag){
6978             cfg.tag = this.tag;
6979         }
6980         
6981         if (this.html) {
6982             cfg.html=this.html
6983         }
6984         if (this.cls) {
6985             cfg.cls=this.cls
6986         }
6987         if (this.abbr) {
6988             cfg.abbr=this.abbr
6989         }
6990         if (this.align) {
6991             cfg.align=this.align
6992         }
6993         if (this.axis) {
6994             cfg.axis=this.axis
6995         }
6996         if (this.bgcolor) {
6997             cfg.bgcolor=this.bgcolor
6998         }
6999         if (this.charoff) {
7000             cfg.charoff=this.charoff
7001         }
7002         if (this.colspan) {
7003             cfg.colspan=this.colspan
7004         }
7005         if (this.headers) {
7006             cfg.headers=this.headers
7007         }
7008         if (this.height) {
7009             cfg.height=this.height
7010         }
7011         if (this.nowrap) {
7012             cfg.nowrap=this.nowrap
7013         }
7014         if (this.rowspan) {
7015             cfg.rowspan=this.rowspan
7016         }
7017         if (this.scope) {
7018             cfg.scope=this.scope
7019         }
7020         if (this.valign) {
7021             cfg.valign=this.valign
7022         }
7023         if (this.width) {
7024             cfg.width=this.width
7025         }
7026         
7027         
7028         return cfg;
7029     }
7030    
7031 });
7032
7033  
7034
7035  /*
7036  * - LGPL
7037  *
7038  * table row
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.TableRow
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap TableRow class
7046  * @cfg {String} cls row class
7047  * @cfg {String} align Aligns the content in a table row
7048  * @cfg {String} bgcolor Specifies a background color for a table row
7049  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7050  * @cfg {String} valign Vertical aligns the content in a table row
7051  * 
7052  * @constructor
7053  * Create a new TableRow
7054  * @param {Object} config The config object
7055  */
7056
7057 Roo.bootstrap.TableRow = function(config){
7058     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7059 };
7060
7061 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7062     
7063     cls: false,
7064     align: false,
7065     bgcolor: false,
7066     charoff: false,
7067     valign: false,
7068     
7069     getAutoCreate : function(){
7070         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7071         
7072         cfg = {
7073             tag: 'tr'
7074         };
7075             
7076         if(this.cls){
7077             cfg.cls = this.cls;
7078         }
7079         if(this.align){
7080             cfg.align = this.align;
7081         }
7082         if(this.bgcolor){
7083             cfg.bgcolor = this.bgcolor;
7084         }
7085         if(this.charoff){
7086             cfg.charoff = this.charoff;
7087         }
7088         if(this.valign){
7089             cfg.valign = this.valign;
7090         }
7091         
7092         return cfg;
7093     }
7094    
7095 });
7096
7097  
7098
7099  /*
7100  * - LGPL
7101  *
7102  * table body
7103  * 
7104  */
7105
7106 /**
7107  * @class Roo.bootstrap.TableBody
7108  * @extends Roo.bootstrap.Component
7109  * Bootstrap TableBody class
7110  * @cfg {String} cls element class
7111  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7112  * @cfg {String} align Aligns the content inside the element
7113  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7114  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7115  * 
7116  * @constructor
7117  * Create a new TableBody
7118  * @param {Object} config The config object
7119  */
7120
7121 Roo.bootstrap.TableBody = function(config){
7122     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7123 };
7124
7125 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7126     
7127     cls: false,
7128     tag: false,
7129     align: false,
7130     charoff: false,
7131     valign: false,
7132     
7133     getAutoCreate : function(){
7134         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7135         
7136         cfg = {
7137             tag: 'tbody'
7138         };
7139             
7140         if (this.cls) {
7141             cfg.cls=this.cls
7142         }
7143         if(this.tag){
7144             cfg.tag = this.tag;
7145         }
7146         
7147         if(this.align){
7148             cfg.align = this.align;
7149         }
7150         if(this.charoff){
7151             cfg.charoff = this.charoff;
7152         }
7153         if(this.valign){
7154             cfg.valign = this.valign;
7155         }
7156         
7157         return cfg;
7158     }
7159     
7160     
7161 //    initEvents : function()
7162 //    {
7163 //        
7164 //        if(!this.store){
7165 //            return;
7166 //        }
7167 //        
7168 //        this.store = Roo.factory(this.store, Roo.data);
7169 //        this.store.on('load', this.onLoad, this);
7170 //        
7171 //        this.store.load();
7172 //        
7173 //    },
7174 //    
7175 //    onLoad: function () 
7176 //    {   
7177 //        this.fireEvent('load', this);
7178 //    }
7179 //    
7180 //   
7181 });
7182
7183  
7184
7185  /*
7186  * Based on:
7187  * Ext JS Library 1.1.1
7188  * Copyright(c) 2006-2007, Ext JS, LLC.
7189  *
7190  * Originally Released Under LGPL - original licence link has changed is not relivant.
7191  *
7192  * Fork - LGPL
7193  * <script type="text/javascript">
7194  */
7195
7196 // as we use this in bootstrap.
7197 Roo.namespace('Roo.form');
7198  /**
7199  * @class Roo.form.Action
7200  * Internal Class used to handle form actions
7201  * @constructor
7202  * @param {Roo.form.BasicForm} el The form element or its id
7203  * @param {Object} config Configuration options
7204  */
7205
7206  
7207  
7208 // define the action interface
7209 Roo.form.Action = function(form, options){
7210     this.form = form;
7211     this.options = options || {};
7212 };
7213 /**
7214  * Client Validation Failed
7215  * @const 
7216  */
7217 Roo.form.Action.CLIENT_INVALID = 'client';
7218 /**
7219  * Server Validation Failed
7220  * @const 
7221  */
7222 Roo.form.Action.SERVER_INVALID = 'server';
7223  /**
7224  * Connect to Server Failed
7225  * @const 
7226  */
7227 Roo.form.Action.CONNECT_FAILURE = 'connect';
7228 /**
7229  * Reading Data from Server Failed
7230  * @const 
7231  */
7232 Roo.form.Action.LOAD_FAILURE = 'load';
7233
7234 Roo.form.Action.prototype = {
7235     type : 'default',
7236     failureType : undefined,
7237     response : undefined,
7238     result : undefined,
7239
7240     // interface method
7241     run : function(options){
7242
7243     },
7244
7245     // interface method
7246     success : function(response){
7247
7248     },
7249
7250     // interface method
7251     handleResponse : function(response){
7252
7253     },
7254
7255     // default connection failure
7256     failure : function(response){
7257         
7258         this.response = response;
7259         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7260         this.form.afterAction(this, false);
7261     },
7262
7263     processResponse : function(response){
7264         this.response = response;
7265         if(!response.responseText){
7266             return true;
7267         }
7268         this.result = this.handleResponse(response);
7269         return this.result;
7270     },
7271
7272     // utility functions used internally
7273     getUrl : function(appendParams){
7274         var url = this.options.url || this.form.url || this.form.el.dom.action;
7275         if(appendParams){
7276             var p = this.getParams();
7277             if(p){
7278                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7279             }
7280         }
7281         return url;
7282     },
7283
7284     getMethod : function(){
7285         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7286     },
7287
7288     getParams : function(){
7289         var bp = this.form.baseParams;
7290         var p = this.options.params;
7291         if(p){
7292             if(typeof p == "object"){
7293                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7294             }else if(typeof p == 'string' && bp){
7295                 p += '&' + Roo.urlEncode(bp);
7296             }
7297         }else if(bp){
7298             p = Roo.urlEncode(bp);
7299         }
7300         return p;
7301     },
7302
7303     createCallback : function(){
7304         return {
7305             success: this.success,
7306             failure: this.failure,
7307             scope: this,
7308             timeout: (this.form.timeout*1000),
7309             upload: this.form.fileUpload ? this.success : undefined
7310         };
7311     }
7312 };
7313
7314 Roo.form.Action.Submit = function(form, options){
7315     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7316 };
7317
7318 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7319     type : 'submit',
7320
7321     haveProgress : false,
7322     uploadComplete : false,
7323     
7324     // uploadProgress indicator.
7325     uploadProgress : function()
7326     {
7327         if (!this.form.progressUrl) {
7328             return;
7329         }
7330         
7331         if (!this.haveProgress) {
7332             Roo.MessageBox.progress("Uploading", "Uploading");
7333         }
7334         if (this.uploadComplete) {
7335            Roo.MessageBox.hide();
7336            return;
7337         }
7338         
7339         this.haveProgress = true;
7340    
7341         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7342         
7343         var c = new Roo.data.Connection();
7344         c.request({
7345             url : this.form.progressUrl,
7346             params: {
7347                 id : uid
7348             },
7349             method: 'GET',
7350             success : function(req){
7351                //console.log(data);
7352                 var rdata = false;
7353                 var edata;
7354                 try  {
7355                    rdata = Roo.decode(req.responseText)
7356                 } catch (e) {
7357                     Roo.log("Invalid data from server..");
7358                     Roo.log(edata);
7359                     return;
7360                 }
7361                 if (!rdata || !rdata.success) {
7362                     Roo.log(rdata);
7363                     Roo.MessageBox.alert(Roo.encode(rdata));
7364                     return;
7365                 }
7366                 var data = rdata.data;
7367                 
7368                 if (this.uploadComplete) {
7369                    Roo.MessageBox.hide();
7370                    return;
7371                 }
7372                    
7373                 if (data){
7374                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7375                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7376                     );
7377                 }
7378                 this.uploadProgress.defer(2000,this);
7379             },
7380        
7381             failure: function(data) {
7382                 Roo.log('progress url failed ');
7383                 Roo.log(data);
7384             },
7385             scope : this
7386         });
7387            
7388     },
7389     
7390     
7391     run : function()
7392     {
7393         // run get Values on the form, so it syncs any secondary forms.
7394         this.form.getValues();
7395         
7396         var o = this.options;
7397         var method = this.getMethod();
7398         var isPost = method == 'POST';
7399         if(o.clientValidation === false || this.form.isValid()){
7400             
7401             if (this.form.progressUrl) {
7402                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7403                     (new Date() * 1) + '' + Math.random());
7404                     
7405             } 
7406             
7407             
7408             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7409                 form:this.form.el.dom,
7410                 url:this.getUrl(!isPost),
7411                 method: method,
7412                 params:isPost ? this.getParams() : null,
7413                 isUpload: this.form.fileUpload
7414             }));
7415             
7416             this.uploadProgress();
7417
7418         }else if (o.clientValidation !== false){ // client validation failed
7419             this.failureType = Roo.form.Action.CLIENT_INVALID;
7420             this.form.afterAction(this, false);
7421         }
7422     },
7423
7424     success : function(response)
7425     {
7426         this.uploadComplete= true;
7427         if (this.haveProgress) {
7428             Roo.MessageBox.hide();
7429         }
7430         
7431         
7432         var result = this.processResponse(response);
7433         if(result === true || result.success){
7434             this.form.afterAction(this, true);
7435             return;
7436         }
7437         if(result.errors){
7438             this.form.markInvalid(result.errors);
7439             this.failureType = Roo.form.Action.SERVER_INVALID;
7440         }
7441         this.form.afterAction(this, false);
7442     },
7443     failure : function(response)
7444     {
7445         this.uploadComplete= true;
7446         if (this.haveProgress) {
7447             Roo.MessageBox.hide();
7448         }
7449         
7450         this.response = response;
7451         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7452         this.form.afterAction(this, false);
7453     },
7454     
7455     handleResponse : function(response){
7456         if(this.form.errorReader){
7457             var rs = this.form.errorReader.read(response);
7458             var errors = [];
7459             if(rs.records){
7460                 for(var i = 0, len = rs.records.length; i < len; i++) {
7461                     var r = rs.records[i];
7462                     errors[i] = r.data;
7463                 }
7464             }
7465             if(errors.length < 1){
7466                 errors = null;
7467             }
7468             return {
7469                 success : rs.success,
7470                 errors : errors
7471             };
7472         }
7473         var ret = false;
7474         try {
7475             ret = Roo.decode(response.responseText);
7476         } catch (e) {
7477             ret = {
7478                 success: false,
7479                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7480                 errors : []
7481             };
7482         }
7483         return ret;
7484         
7485     }
7486 });
7487
7488
7489 Roo.form.Action.Load = function(form, options){
7490     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7491     this.reader = this.form.reader;
7492 };
7493
7494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7495     type : 'load',
7496
7497     run : function(){
7498         
7499         Roo.Ajax.request(Roo.apply(
7500                 this.createCallback(), {
7501                     method:this.getMethod(),
7502                     url:this.getUrl(false),
7503                     params:this.getParams()
7504         }));
7505     },
7506
7507     success : function(response){
7508         
7509         var result = this.processResponse(response);
7510         if(result === true || !result.success || !result.data){
7511             this.failureType = Roo.form.Action.LOAD_FAILURE;
7512             this.form.afterAction(this, false);
7513             return;
7514         }
7515         this.form.clearInvalid();
7516         this.form.setValues(result.data);
7517         this.form.afterAction(this, true);
7518     },
7519
7520     handleResponse : function(response){
7521         if(this.form.reader){
7522             var rs = this.form.reader.read(response);
7523             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7524             return {
7525                 success : rs.success,
7526                 data : data
7527             };
7528         }
7529         return Roo.decode(response.responseText);
7530     }
7531 });
7532
7533 Roo.form.Action.ACTION_TYPES = {
7534     'load' : Roo.form.Action.Load,
7535     'submit' : Roo.form.Action.Submit
7536 };/*
7537  * - LGPL
7538  *
7539  * form
7540  *
7541  */
7542
7543 /**
7544  * @class Roo.bootstrap.Form
7545  * @extends Roo.bootstrap.Component
7546  * Bootstrap Form class
7547  * @cfg {String} method  GET | POST (default POST)
7548  * @cfg {String} labelAlign top | left (default top)
7549  * @cfg {String} align left  | right - for navbars
7550  * @cfg {Boolean} loadMask load mask when submit (default true)
7551
7552  *
7553  * @constructor
7554  * Create a new Form
7555  * @param {Object} config The config object
7556  */
7557
7558
7559 Roo.bootstrap.Form = function(config){
7560     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7561     
7562     Roo.bootstrap.Form.popover.apply();
7563     
7564     this.addEvents({
7565         /**
7566          * @event clientvalidation
7567          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7568          * @param {Form} this
7569          * @param {Boolean} valid true if the form has passed client-side validation
7570          */
7571         clientvalidation: true,
7572         /**
7573          * @event beforeaction
7574          * Fires before any action is performed. Return false to cancel the action.
7575          * @param {Form} this
7576          * @param {Action} action The action to be performed
7577          */
7578         beforeaction: true,
7579         /**
7580          * @event actionfailed
7581          * Fires when an action fails.
7582          * @param {Form} this
7583          * @param {Action} action The action that failed
7584          */
7585         actionfailed : true,
7586         /**
7587          * @event actioncomplete
7588          * Fires when an action is completed.
7589          * @param {Form} this
7590          * @param {Action} action The action that completed
7591          */
7592         actioncomplete : true
7593     });
7594
7595 };
7596
7597 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7598
7599      /**
7600      * @cfg {String} method
7601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7602      */
7603     method : 'POST',
7604     /**
7605      * @cfg {String} url
7606      * The URL to use for form actions if one isn't supplied in the action options.
7607      */
7608     /**
7609      * @cfg {Boolean} fileUpload
7610      * Set to true if this form is a file upload.
7611      */
7612
7613     /**
7614      * @cfg {Object} baseParams
7615      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7616      */
7617
7618     /**
7619      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7620      */
7621     timeout: 30,
7622     /**
7623      * @cfg {Sting} align (left|right) for navbar forms
7624      */
7625     align : 'left',
7626
7627     // private
7628     activeAction : null,
7629
7630     /**
7631      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7632      * element by passing it or its id or mask the form itself by passing in true.
7633      * @type Mixed
7634      */
7635     waitMsgTarget : false,
7636
7637     loadMask : true,
7638     
7639     /**
7640      * @cfg {Boolean} errorMask (true|false) default false
7641      */
7642     errorMask : false,
7643     
7644     /**
7645      * @cfg {Number} maskOffset Default 100
7646      */
7647     maskOffset : 100,
7648     
7649     /**
7650      * @cfg {Boolean} maskBody
7651      */
7652     maskBody : false,
7653
7654     getAutoCreate : function(){
7655
7656         var cfg = {
7657             tag: 'form',
7658             method : this.method || 'POST',
7659             id : this.id || Roo.id(),
7660             cls : ''
7661         };
7662         if (this.parent().xtype.match(/^Nav/)) {
7663             cfg.cls = 'navbar-form navbar-' + this.align;
7664
7665         }
7666
7667         if (this.labelAlign == 'left' ) {
7668             cfg.cls += ' form-horizontal';
7669         }
7670
7671
7672         return cfg;
7673     },
7674     initEvents : function()
7675     {
7676         this.el.on('submit', this.onSubmit, this);
7677         // this was added as random key presses on the form where triggering form submit.
7678         this.el.on('keypress', function(e) {
7679             if (e.getCharCode() != 13) {
7680                 return true;
7681             }
7682             // we might need to allow it for textareas.. and some other items.
7683             // check e.getTarget().
7684
7685             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7686                 return true;
7687             }
7688
7689             Roo.log("keypress blocked");
7690
7691             e.preventDefault();
7692             return false;
7693         });
7694         
7695     },
7696     // private
7697     onSubmit : function(e){
7698         e.stopEvent();
7699     },
7700
7701      /**
7702      * Returns true if client-side validation on the form is successful.
7703      * @return Boolean
7704      */
7705     isValid : function(){
7706         var items = this.getItems();
7707         var valid = true;
7708         var target = false;
7709         
7710         items.each(function(f){
7711             if(f.validate()){
7712                 return;
7713             }
7714             valid = false;
7715
7716             if(!target && f.el.isVisible(true)){
7717                 target = f;
7718             }
7719            
7720         });
7721         
7722         if(this.errorMask && !valid){
7723             Roo.bootstrap.Form.popover.mask(this, target);
7724         }
7725         
7726         return valid;
7727     },
7728     
7729     /**
7730      * Returns true if any fields in this form have changed since their original load.
7731      * @return Boolean
7732      */
7733     isDirty : function(){
7734         var dirty = false;
7735         var items = this.getItems();
7736         items.each(function(f){
7737            if(f.isDirty()){
7738                dirty = true;
7739                return false;
7740            }
7741            return true;
7742         });
7743         return dirty;
7744     },
7745      /**
7746      * Performs a predefined action (submit or load) or custom actions you define on this form.
7747      * @param {String} actionName The name of the action type
7748      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7749      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7750      * accept other config options):
7751      * <pre>
7752 Property          Type             Description
7753 ----------------  ---------------  ----------------------------------------------------------------------------------
7754 url               String           The url for the action (defaults to the form's url)
7755 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7756 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7757 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7758                                    validate the form on the client (defaults to false)
7759      * </pre>
7760      * @return {BasicForm} this
7761      */
7762     doAction : function(action, options){
7763         if(typeof action == 'string'){
7764             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7765         }
7766         if(this.fireEvent('beforeaction', this, action) !== false){
7767             this.beforeAction(action);
7768             action.run.defer(100, action);
7769         }
7770         return this;
7771     },
7772
7773     // private
7774     beforeAction : function(action){
7775         var o = action.options;
7776         
7777         if(this.loadMask){
7778             
7779             if(this.maskBody){
7780                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7781             } else {
7782                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7783             }
7784         }
7785         // not really supported yet.. ??
7786
7787         //if(this.waitMsgTarget === true){
7788         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7789         //}else if(this.waitMsgTarget){
7790         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7791         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7792         //}else {
7793         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7794        // }
7795
7796     },
7797
7798     // private
7799     afterAction : function(action, success){
7800         this.activeAction = null;
7801         var o = action.options;
7802
7803         if(this.loadMask){
7804             
7805             if(this.maskBody){
7806                 Roo.get(document.body).unmask();
7807             } else {
7808                 this.el.unmask();
7809             }
7810         }
7811         
7812         //if(this.waitMsgTarget === true){
7813 //            this.el.unmask();
7814         //}else if(this.waitMsgTarget){
7815         //    this.waitMsgTarget.unmask();
7816         //}else{
7817         //    Roo.MessageBox.updateProgress(1);
7818         //    Roo.MessageBox.hide();
7819        // }
7820         //
7821         if(success){
7822             if(o.reset){
7823                 this.reset();
7824             }
7825             Roo.callback(o.success, o.scope, [this, action]);
7826             this.fireEvent('actioncomplete', this, action);
7827
7828         }else{
7829
7830             // failure condition..
7831             // we have a scenario where updates need confirming.
7832             // eg. if a locking scenario exists..
7833             // we look for { errors : { needs_confirm : true }} in the response.
7834             if (
7835                 (typeof(action.result) != 'undefined')  &&
7836                 (typeof(action.result.errors) != 'undefined')  &&
7837                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7838            ){
7839                 var _t = this;
7840                 Roo.log("not supported yet");
7841                  /*
7842
7843                 Roo.MessageBox.confirm(
7844                     "Change requires confirmation",
7845                     action.result.errorMsg,
7846                     function(r) {
7847                         if (r != 'yes') {
7848                             return;
7849                         }
7850                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7851                     }
7852
7853                 );
7854                 */
7855
7856
7857                 return;
7858             }
7859
7860             Roo.callback(o.failure, o.scope, [this, action]);
7861             // show an error message if no failed handler is set..
7862             if (!this.hasListener('actionfailed')) {
7863                 Roo.log("need to add dialog support");
7864                 /*
7865                 Roo.MessageBox.alert("Error",
7866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7867                         action.result.errorMsg :
7868                         "Saving Failed, please check your entries or try again"
7869                 );
7870                 */
7871             }
7872
7873             this.fireEvent('actionfailed', this, action);
7874         }
7875
7876     },
7877     /**
7878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7879      * @param {String} id The value to search for
7880      * @return Field
7881      */
7882     findField : function(id){
7883         var items = this.getItems();
7884         var field = items.get(id);
7885         if(!field){
7886              items.each(function(f){
7887                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7888                     field = f;
7889                     return false;
7890                 }
7891                 return true;
7892             });
7893         }
7894         return field || null;
7895     },
7896      /**
7897      * Mark fields in this form invalid in bulk.
7898      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7899      * @return {BasicForm} this
7900      */
7901     markInvalid : function(errors){
7902         if(errors instanceof Array){
7903             for(var i = 0, len = errors.length; i < len; i++){
7904                 var fieldError = errors[i];
7905                 var f = this.findField(fieldError.id);
7906                 if(f){
7907                     f.markInvalid(fieldError.msg);
7908                 }
7909             }
7910         }else{
7911             var field, id;
7912             for(id in errors){
7913                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7914                     field.markInvalid(errors[id]);
7915                 }
7916             }
7917         }
7918         //Roo.each(this.childForms || [], function (f) {
7919         //    f.markInvalid(errors);
7920         //});
7921
7922         return this;
7923     },
7924
7925     /**
7926      * Set values for fields in this form in bulk.
7927      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7928      * @return {BasicForm} this
7929      */
7930     setValues : function(values){
7931         if(values instanceof Array){ // array of objects
7932             for(var i = 0, len = values.length; i < len; i++){
7933                 var v = values[i];
7934                 var f = this.findField(v.id);
7935                 if(f){
7936                     f.setValue(v.value);
7937                     if(this.trackResetOnLoad){
7938                         f.originalValue = f.getValue();
7939                     }
7940                 }
7941             }
7942         }else{ // object hash
7943             var field, id;
7944             for(id in values){
7945                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7946
7947                     if (field.setFromData &&
7948                         field.valueField &&
7949                         field.displayField &&
7950                         // combos' with local stores can
7951                         // be queried via setValue()
7952                         // to set their value..
7953                         (field.store && !field.store.isLocal)
7954                         ) {
7955                         // it's a combo
7956                         var sd = { };
7957                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7958                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7959                         field.setFromData(sd);
7960
7961                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7962                         
7963                         field.setFromData(values);
7964                         
7965                     } else {
7966                         field.setValue(values[id]);
7967                     }
7968
7969
7970                     if(this.trackResetOnLoad){
7971                         field.originalValue = field.getValue();
7972                     }
7973                 }
7974             }
7975         }
7976
7977         //Roo.each(this.childForms || [], function (f) {
7978         //    f.setValues(values);
7979         //});
7980
7981         return this;
7982     },
7983
7984     /**
7985      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7986      * they are returned as an array.
7987      * @param {Boolean} asString
7988      * @return {Object}
7989      */
7990     getValues : function(asString){
7991         //if (this.childForms) {
7992             // copy values from the child forms
7993         //    Roo.each(this.childForms, function (f) {
7994         //        this.setValues(f.getValues());
7995         //    }, this);
7996         //}
7997
7998
7999
8000         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8001         if(asString === true){
8002             return fs;
8003         }
8004         return Roo.urlDecode(fs);
8005     },
8006
8007     /**
8008      * Returns the fields in this form as an object with key/value pairs.
8009      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8010      * @return {Object}
8011      */
8012     getFieldValues : function(with_hidden)
8013     {
8014         var items = this.getItems();
8015         var ret = {};
8016         items.each(function(f){
8017             
8018             if (!f.getName()) {
8019                 return;
8020             }
8021             
8022             var v = f.getValue();
8023             
8024             if (f.inputType =='radio') {
8025                 if (typeof(ret[f.getName()]) == 'undefined') {
8026                     ret[f.getName()] = ''; // empty..
8027                 }
8028
8029                 if (!f.el.dom.checked) {
8030                     return;
8031
8032                 }
8033                 v = f.el.dom.value;
8034
8035             }
8036             
8037             if(f.xtype == 'MoneyField'){
8038                 ret[f.currencyName] = f.getCurrency();
8039             }
8040
8041             // not sure if this supported any more..
8042             if ((typeof(v) == 'object') && f.getRawValue) {
8043                 v = f.getRawValue() ; // dates..
8044             }
8045             // combo boxes where name != hiddenName...
8046             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8047                 ret[f.name] = f.getRawValue();
8048             }
8049             ret[f.getName()] = v;
8050         });
8051
8052         return ret;
8053     },
8054
8055     /**
8056      * Clears all invalid messages in this form.
8057      * @return {BasicForm} this
8058      */
8059     clearInvalid : function(){
8060         var items = this.getItems();
8061
8062         items.each(function(f){
8063            f.clearInvalid();
8064         });
8065
8066         return this;
8067     },
8068
8069     /**
8070      * Resets this form.
8071      * @return {BasicForm} this
8072      */
8073     reset : function(){
8074         var items = this.getItems();
8075         items.each(function(f){
8076             f.reset();
8077         });
8078
8079         Roo.each(this.childForms || [], function (f) {
8080             f.reset();
8081         });
8082
8083
8084         return this;
8085     },
8086     
8087     getItems : function()
8088     {
8089         var r=new Roo.util.MixedCollection(false, function(o){
8090             return o.id || (o.id = Roo.id());
8091         });
8092         var iter = function(el) {
8093             if (el.inputEl) {
8094                 r.add(el);
8095             }
8096             if (!el.items) {
8097                 return;
8098             }
8099             Roo.each(el.items,function(e) {
8100                 iter(e);
8101             });
8102
8103
8104         };
8105
8106         iter(this);
8107         return r;
8108         
8109     }
8110
8111 });
8112
8113 Roo.apply(Roo.bootstrap.Form, {
8114     
8115     popover : {
8116         
8117         padding : 5,
8118         
8119         isApplied : false,
8120         
8121         isMasked : false,
8122         
8123         form : false,
8124         
8125         target : false,
8126         
8127         toolTip : false,
8128         
8129         intervalID : false,
8130         
8131         maskEl : false,
8132         
8133         apply : function()
8134         {
8135             if(this.isApplied){
8136                 return;
8137             }
8138             
8139             this.maskEl = {
8140                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8141                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8142                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8143                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8144             };
8145             
8146             this.maskEl.top.enableDisplayMode("block");
8147             this.maskEl.left.enableDisplayMode("block");
8148             this.maskEl.bottom.enableDisplayMode("block");
8149             this.maskEl.right.enableDisplayMode("block");
8150             
8151             this.toolTip = new Roo.bootstrap.Tooltip({
8152                 cls : 'roo-form-error-popover',
8153                 alignment : {
8154                     'left' : ['r-l', [-2,0], 'right'],
8155                     'right' : ['l-r', [2,0], 'left'],
8156                     'bottom' : ['tl-bl', [0,2], 'top'],
8157                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8158                 }
8159             });
8160             
8161             this.toolTip.render(Roo.get(document.body));
8162
8163             this.toolTip.el.enableDisplayMode("block");
8164             
8165             Roo.get(document.body).on('click', function(){
8166                 this.unmask();
8167             }, this);
8168             
8169             Roo.get(document.body).on('touchstart', function(){
8170                 this.unmask();
8171             }, this);
8172             
8173             this.isApplied = true
8174         },
8175         
8176         mask : function(form, target)
8177         {
8178             this.form = form;
8179             
8180             this.target = target;
8181             
8182             if(!this.form.errorMask || !target.el){
8183                 return;
8184             }
8185             
8186             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8187             
8188             Roo.log(scrollable);
8189             
8190             var ot = this.target.el.calcOffsetsTo(scrollable);
8191             
8192             var scrollTo = ot[1] - this.form.maskOffset;
8193             
8194             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8195             
8196             scrollable.scrollTo('top', scrollTo);
8197             
8198             var box = this.target.el.getBox();
8199             Roo.log(box);
8200             var zIndex = Roo.bootstrap.Modal.zIndex++;
8201
8202             
8203             this.maskEl.top.setStyle('position', 'absolute');
8204             this.maskEl.top.setStyle('z-index', zIndex);
8205             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8206             this.maskEl.top.setLeft(0);
8207             this.maskEl.top.setTop(0);
8208             this.maskEl.top.show();
8209             
8210             this.maskEl.left.setStyle('position', 'absolute');
8211             this.maskEl.left.setStyle('z-index', zIndex);
8212             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8213             this.maskEl.left.setLeft(0);
8214             this.maskEl.left.setTop(box.y - this.padding);
8215             this.maskEl.left.show();
8216
8217             this.maskEl.bottom.setStyle('position', 'absolute');
8218             this.maskEl.bottom.setStyle('z-index', zIndex);
8219             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8220             this.maskEl.bottom.setLeft(0);
8221             this.maskEl.bottom.setTop(box.bottom + this.padding);
8222             this.maskEl.bottom.show();
8223
8224             this.maskEl.right.setStyle('position', 'absolute');
8225             this.maskEl.right.setStyle('z-index', zIndex);
8226             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8227             this.maskEl.right.setLeft(box.right + this.padding);
8228             this.maskEl.right.setTop(box.y - this.padding);
8229             this.maskEl.right.show();
8230
8231             this.toolTip.bindEl = this.target.el;
8232
8233             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8234
8235             var tip = this.target.blankText;
8236
8237             if(this.target.getValue() !== '' ) {
8238                 
8239                 if (this.target.invalidText.length) {
8240                     tip = this.target.invalidText;
8241                 } else if (this.target.regexText.length){
8242                     tip = this.target.regexText;
8243                 }
8244             }
8245
8246             this.toolTip.show(tip);
8247
8248             this.intervalID = window.setInterval(function() {
8249                 Roo.bootstrap.Form.popover.unmask();
8250             }, 10000);
8251
8252             window.onwheel = function(){ return false;};
8253             
8254             (function(){ this.isMasked = true; }).defer(500, this);
8255             
8256         },
8257         
8258         unmask : function()
8259         {
8260             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8261                 return;
8262             }
8263             
8264             this.maskEl.top.setStyle('position', 'absolute');
8265             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8266             this.maskEl.top.hide();
8267
8268             this.maskEl.left.setStyle('position', 'absolute');
8269             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8270             this.maskEl.left.hide();
8271
8272             this.maskEl.bottom.setStyle('position', 'absolute');
8273             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8274             this.maskEl.bottom.hide();
8275
8276             this.maskEl.right.setStyle('position', 'absolute');
8277             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8278             this.maskEl.right.hide();
8279             
8280             this.toolTip.hide();
8281             
8282             this.toolTip.el.hide();
8283             
8284             window.onwheel = function(){ return true;};
8285             
8286             if(this.intervalID){
8287                 window.clearInterval(this.intervalID);
8288                 this.intervalID = false;
8289             }
8290             
8291             this.isMasked = false;
8292             
8293         }
8294         
8295     }
8296     
8297 });
8298
8299 /*
8300  * Based on:
8301  * Ext JS Library 1.1.1
8302  * Copyright(c) 2006-2007, Ext JS, LLC.
8303  *
8304  * Originally Released Under LGPL - original licence link has changed is not relivant.
8305  *
8306  * Fork - LGPL
8307  * <script type="text/javascript">
8308  */
8309 /**
8310  * @class Roo.form.VTypes
8311  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8312  * @singleton
8313  */
8314 Roo.form.VTypes = function(){
8315     // closure these in so they are only created once.
8316     var alpha = /^[a-zA-Z_]+$/;
8317     var alphanum = /^[a-zA-Z0-9_]+$/;
8318     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8319     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8320
8321     // All these messages and functions are configurable
8322     return {
8323         /**
8324          * The function used to validate email addresses
8325          * @param {String} value The email address
8326          */
8327         'email' : function(v){
8328             return email.test(v);
8329         },
8330         /**
8331          * The error text to display when the email validation function returns false
8332          * @type String
8333          */
8334         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8335         /**
8336          * The keystroke filter mask to be applied on email input
8337          * @type RegExp
8338          */
8339         'emailMask' : /[a-z0-9_\.\-@]/i,
8340
8341         /**
8342          * The function used to validate URLs
8343          * @param {String} value The URL
8344          */
8345         'url' : function(v){
8346             return url.test(v);
8347         },
8348         /**
8349          * The error text to display when the url validation function returns false
8350          * @type String
8351          */
8352         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8353         
8354         /**
8355          * The function used to validate alpha values
8356          * @param {String} value The value
8357          */
8358         'alpha' : function(v){
8359             return alpha.test(v);
8360         },
8361         /**
8362          * The error text to display when the alpha validation function returns false
8363          * @type String
8364          */
8365         'alphaText' : 'This field should only contain letters and _',
8366         /**
8367          * The keystroke filter mask to be applied on alpha input
8368          * @type RegExp
8369          */
8370         'alphaMask' : /[a-z_]/i,
8371
8372         /**
8373          * The function used to validate alphanumeric values
8374          * @param {String} value The value
8375          */
8376         'alphanum' : function(v){
8377             return alphanum.test(v);
8378         },
8379         /**
8380          * The error text to display when the alphanumeric validation function returns false
8381          * @type String
8382          */
8383         'alphanumText' : 'This field should only contain letters, numbers and _',
8384         /**
8385          * The keystroke filter mask to be applied on alphanumeric input
8386          * @type RegExp
8387          */
8388         'alphanumMask' : /[a-z0-9_]/i
8389     };
8390 }();/*
8391  * - LGPL
8392  *
8393  * Input
8394  * 
8395  */
8396
8397 /**
8398  * @class Roo.bootstrap.Input
8399  * @extends Roo.bootstrap.Component
8400  * Bootstrap Input class
8401  * @cfg {Boolean} disabled is it disabled
8402  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8403  * @cfg {String} name name of the input
8404  * @cfg {string} fieldLabel - the label associated
8405  * @cfg {string} placeholder - placeholder to put in text.
8406  * @cfg {string}  before - input group add on before
8407  * @cfg {string} after - input group add on after
8408  * @cfg {string} size - (lg|sm) or leave empty..
8409  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8410  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8411  * @cfg {Number} md colspan out of 12 for computer-sized screens
8412  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8413  * @cfg {string} value default value of the input
8414  * @cfg {Number} labelWidth set the width of label 
8415  * @cfg {Number} labellg set the width of label (1-12)
8416  * @cfg {Number} labelmd set the width of label (1-12)
8417  * @cfg {Number} labelsm set the width of label (1-12)
8418  * @cfg {Number} labelxs set the width of label (1-12)
8419  * @cfg {String} labelAlign (top|left)
8420  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8421  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8422  * @cfg {String} indicatorpos (left|right) default left
8423
8424  * @cfg {String} align (left|center|right) Default left
8425  * @cfg {Boolean} forceFeedback (true|false) Default false
8426  * 
8427  * 
8428  * 
8429  * 
8430  * @constructor
8431  * Create a new Input
8432  * @param {Object} config The config object
8433  */
8434
8435 Roo.bootstrap.Input = function(config){
8436     
8437     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8438     
8439     this.addEvents({
8440         /**
8441          * @event focus
8442          * Fires when this field receives input focus.
8443          * @param {Roo.form.Field} this
8444          */
8445         focus : true,
8446         /**
8447          * @event blur
8448          * Fires when this field loses input focus.
8449          * @param {Roo.form.Field} this
8450          */
8451         blur : true,
8452         /**
8453          * @event specialkey
8454          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8455          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8456          * @param {Roo.form.Field} this
8457          * @param {Roo.EventObject} e The event object
8458          */
8459         specialkey : true,
8460         /**
8461          * @event change
8462          * Fires just before the field blurs if the field value has changed.
8463          * @param {Roo.form.Field} this
8464          * @param {Mixed} newValue The new value
8465          * @param {Mixed} oldValue The original value
8466          */
8467         change : true,
8468         /**
8469          * @event invalid
8470          * Fires after the field has been marked as invalid.
8471          * @param {Roo.form.Field} this
8472          * @param {String} msg The validation message
8473          */
8474         invalid : true,
8475         /**
8476          * @event valid
8477          * Fires after the field has been validated with no errors.
8478          * @param {Roo.form.Field} this
8479          */
8480         valid : true,
8481          /**
8482          * @event keyup
8483          * Fires after the key up
8484          * @param {Roo.form.Field} this
8485          * @param {Roo.EventObject}  e The event Object
8486          */
8487         keyup : true
8488     });
8489 };
8490
8491 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8492      /**
8493      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8494       automatic validation (defaults to "keyup").
8495      */
8496     validationEvent : "keyup",
8497      /**
8498      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8499      */
8500     validateOnBlur : true,
8501     /**
8502      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8503      */
8504     validationDelay : 250,
8505      /**
8506      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8507      */
8508     focusClass : "x-form-focus",  // not needed???
8509     
8510        
8511     /**
8512      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8513      */
8514     invalidClass : "has-warning",
8515     
8516     /**
8517      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8518      */
8519     validClass : "has-success",
8520     
8521     /**
8522      * @cfg {Boolean} hasFeedback (true|false) default true
8523      */
8524     hasFeedback : true,
8525     
8526     /**
8527      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8528      */
8529     invalidFeedbackClass : "glyphicon-warning-sign",
8530     
8531     /**
8532      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8533      */
8534     validFeedbackClass : "glyphicon-ok",
8535     
8536     /**
8537      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8538      */
8539     selectOnFocus : false,
8540     
8541      /**
8542      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8543      */
8544     maskRe : null,
8545        /**
8546      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8547      */
8548     vtype : null,
8549     
8550       /**
8551      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8552      */
8553     disableKeyFilter : false,
8554     
8555        /**
8556      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8557      */
8558     disabled : false,
8559      /**
8560      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8561      */
8562     allowBlank : true,
8563     /**
8564      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8565      */
8566     blankText : "Please complete this mandatory field",
8567     
8568      /**
8569      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8570      */
8571     minLength : 0,
8572     /**
8573      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8574      */
8575     maxLength : Number.MAX_VALUE,
8576     /**
8577      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8578      */
8579     minLengthText : "The minimum length for this field is {0}",
8580     /**
8581      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8582      */
8583     maxLengthText : "The maximum length for this field is {0}",
8584   
8585     
8586     /**
8587      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8588      * If available, this function will be called only after the basic validators all return true, and will be passed the
8589      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8590      */
8591     validator : null,
8592     /**
8593      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8594      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8595      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8596      */
8597     regex : null,
8598     /**
8599      * @cfg {String} regexText -- Depricated - use Invalid Text
8600      */
8601     regexText : "",
8602     
8603     /**
8604      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8605      */
8606     invalidText : "",
8607     
8608     
8609     
8610     autocomplete: false,
8611     
8612     
8613     fieldLabel : '',
8614     inputType : 'text',
8615     
8616     name : false,
8617     placeholder: false,
8618     before : false,
8619     after : false,
8620     size : false,
8621     hasFocus : false,
8622     preventMark: false,
8623     isFormField : true,
8624     value : '',
8625     labelWidth : 2,
8626     labelAlign : false,
8627     readOnly : false,
8628     align : false,
8629     formatedValue : false,
8630     forceFeedback : false,
8631     
8632     indicatorpos : 'left',
8633     
8634     labellg : 0,
8635     labelmd : 0,
8636     labelsm : 0,
8637     labelxs : 0,
8638     
8639     parentLabelAlign : function()
8640     {
8641         var parent = this;
8642         while (parent.parent()) {
8643             parent = parent.parent();
8644             if (typeof(parent.labelAlign) !='undefined') {
8645                 return parent.labelAlign;
8646             }
8647         }
8648         return 'left';
8649         
8650     },
8651     
8652     getAutoCreate : function()
8653     {
8654         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8655         
8656         var id = Roo.id();
8657         
8658         var cfg = {};
8659         
8660         if(this.inputType != 'hidden'){
8661             cfg.cls = 'form-group' //input-group
8662         }
8663         
8664         var input =  {
8665             tag: 'input',
8666             id : id,
8667             type : this.inputType,
8668             value : this.value,
8669             cls : 'form-control',
8670             placeholder : this.placeholder || '',
8671             autocomplete : this.autocomplete || 'new-password'
8672         };
8673         
8674         if(this.align){
8675             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8676         }
8677         
8678         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8679             input.maxLength = this.maxLength;
8680         }
8681         
8682         if (this.disabled) {
8683             input.disabled=true;
8684         }
8685         
8686         if (this.readOnly) {
8687             input.readonly=true;
8688         }
8689         
8690         if (this.name) {
8691             input.name = this.name;
8692         }
8693         
8694         if (this.size) {
8695             input.cls += ' input-' + this.size;
8696         }
8697         
8698         var settings=this;
8699         ['xs','sm','md','lg'].map(function(size){
8700             if (settings[size]) {
8701                 cfg.cls += ' col-' + size + '-' + settings[size];
8702             }
8703         });
8704         
8705         var inputblock = input;
8706         
8707         var feedback = {
8708             tag: 'span',
8709             cls: 'glyphicon form-control-feedback'
8710         };
8711             
8712         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8713             
8714             inputblock = {
8715                 cls : 'has-feedback',
8716                 cn :  [
8717                     input,
8718                     feedback
8719                 ] 
8720             };  
8721         }
8722         
8723         if (this.before || this.after) {
8724             
8725             inputblock = {
8726                 cls : 'input-group',
8727                 cn :  [] 
8728             };
8729             
8730             if (this.before && typeof(this.before) == 'string') {
8731                 
8732                 inputblock.cn.push({
8733                     tag :'span',
8734                     cls : 'roo-input-before input-group-addon',
8735                     html : this.before
8736                 });
8737             }
8738             if (this.before && typeof(this.before) == 'object') {
8739                 this.before = Roo.factory(this.before);
8740                 
8741                 inputblock.cn.push({
8742                     tag :'span',
8743                     cls : 'roo-input-before input-group-' +
8744                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8745                 });
8746             }
8747             
8748             inputblock.cn.push(input);
8749             
8750             if (this.after && typeof(this.after) == 'string') {
8751                 inputblock.cn.push({
8752                     tag :'span',
8753                     cls : 'roo-input-after input-group-addon',
8754                     html : this.after
8755                 });
8756             }
8757             if (this.after && typeof(this.after) == 'object') {
8758                 this.after = Roo.factory(this.after);
8759                 
8760                 inputblock.cn.push({
8761                     tag :'span',
8762                     cls : 'roo-input-after input-group-' +
8763                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8764                 });
8765             }
8766             
8767             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8768                 inputblock.cls += ' has-feedback';
8769                 inputblock.cn.push(feedback);
8770             }
8771         };
8772         
8773         if (align ==='left' && this.fieldLabel.length) {
8774             
8775             cfg.cls += ' roo-form-group-label-left';
8776             
8777             cfg.cn = [
8778                 {
8779                     tag : 'i',
8780                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8781                     tooltip : 'This field is required'
8782                 },
8783                 {
8784                     tag: 'label',
8785                     'for' :  id,
8786                     cls : 'control-label',
8787                     html : this.fieldLabel
8788
8789                 },
8790                 {
8791                     cls : "", 
8792                     cn: [
8793                         inputblock
8794                     ]
8795                 }
8796             ];
8797             
8798             var labelCfg = cfg.cn[1];
8799             var contentCfg = cfg.cn[2];
8800             
8801             if(this.indicatorpos == 'right'){
8802                 cfg.cn = [
8803                     {
8804                         tag: 'label',
8805                         'for' :  id,
8806                         cls : 'control-label',
8807                         cn : [
8808                             {
8809                                 tag : 'span',
8810                                 html : this.fieldLabel
8811                             },
8812                             {
8813                                 tag : 'i',
8814                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8815                                 tooltip : 'This field is required'
8816                             }
8817                         ]
8818                     },
8819                     {
8820                         cls : "",
8821                         cn: [
8822                             inputblock
8823                         ]
8824                     }
8825
8826                 ];
8827                 
8828                 labelCfg = cfg.cn[0];
8829                 contentCfg = cfg.cn[1];
8830             
8831             }
8832             
8833             if(this.labelWidth > 12){
8834                 labelCfg.style = "width: " + this.labelWidth + 'px';
8835             }
8836             
8837             if(this.labelWidth < 13 && this.labelmd == 0){
8838                 this.labelmd = this.labelWidth;
8839             }
8840             
8841             if(this.labellg > 0){
8842                 labelCfg.cls += ' col-lg-' + this.labellg;
8843                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8844             }
8845             
8846             if(this.labelmd > 0){
8847                 labelCfg.cls += ' col-md-' + this.labelmd;
8848                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8849             }
8850             
8851             if(this.labelsm > 0){
8852                 labelCfg.cls += ' col-sm-' + this.labelsm;
8853                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8854             }
8855             
8856             if(this.labelxs > 0){
8857                 labelCfg.cls += ' col-xs-' + this.labelxs;
8858                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8859             }
8860             
8861             
8862         } else if ( this.fieldLabel.length) {
8863                 
8864             cfg.cn = [
8865                 {
8866                     tag : 'i',
8867                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8868                     tooltip : 'This field is required'
8869                 },
8870                 {
8871                     tag: 'label',
8872                    //cls : 'input-group-addon',
8873                     html : this.fieldLabel
8874
8875                 },
8876
8877                inputblock
8878
8879            ];
8880            
8881            if(this.indicatorpos == 'right'){
8882                 
8883                 cfg.cn = [
8884                     {
8885                         tag: 'label',
8886                        //cls : 'input-group-addon',
8887                         html : this.fieldLabel
8888
8889                     },
8890                     {
8891                         tag : 'i',
8892                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8893                         tooltip : 'This field is required'
8894                     },
8895
8896                    inputblock
8897
8898                ];
8899
8900             }
8901
8902         } else {
8903             
8904             cfg.cn = [
8905
8906                     inputblock
8907
8908             ];
8909                 
8910                 
8911         };
8912         
8913         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8914            cfg.cls += ' navbar-form';
8915         }
8916         
8917         if (this.parentType === 'NavGroup') {
8918            cfg.cls += ' navbar-form';
8919            cfg.tag = 'li';
8920         }
8921         
8922         return cfg;
8923         
8924     },
8925     /**
8926      * return the real input element.
8927      */
8928     inputEl: function ()
8929     {
8930         return this.el.select('input.form-control',true).first();
8931     },
8932     
8933     tooltipEl : function()
8934     {
8935         return this.inputEl();
8936     },
8937     
8938     indicatorEl : function()
8939     {
8940         var indicator = this.el.select('i.roo-required-indicator',true).first();
8941         
8942         if(!indicator){
8943             return false;
8944         }
8945         
8946         return indicator;
8947         
8948     },
8949     
8950     setDisabled : function(v)
8951     {
8952         var i  = this.inputEl().dom;
8953         if (!v) {
8954             i.removeAttribute('disabled');
8955             return;
8956             
8957         }
8958         i.setAttribute('disabled','true');
8959     },
8960     initEvents : function()
8961     {
8962           
8963         this.inputEl().on("keydown" , this.fireKey,  this);
8964         this.inputEl().on("focus", this.onFocus,  this);
8965         this.inputEl().on("blur", this.onBlur,  this);
8966         
8967         this.inputEl().relayEvent('keyup', this);
8968         
8969         this.indicator = this.indicatorEl();
8970         
8971         if(this.indicator){
8972             this.indicator.addClass('invisible');
8973             
8974         }
8975  
8976         // reference to original value for reset
8977         this.originalValue = this.getValue();
8978         //Roo.form.TextField.superclass.initEvents.call(this);
8979         if(this.validationEvent == 'keyup'){
8980             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8981             this.inputEl().on('keyup', this.filterValidation, this);
8982         }
8983         else if(this.validationEvent !== false){
8984             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8985         }
8986         
8987         if(this.selectOnFocus){
8988             this.on("focus", this.preFocus, this);
8989             
8990         }
8991         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8992             this.inputEl().on("keypress", this.filterKeys, this);
8993         } else {
8994             this.inputEl().relayEvent('keypress', this);
8995         }
8996        /* if(this.grow){
8997             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8998             this.el.on("click", this.autoSize,  this);
8999         }
9000         */
9001         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9002             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9003         }
9004         
9005         if (typeof(this.before) == 'object') {
9006             this.before.render(this.el.select('.roo-input-before',true).first());
9007         }
9008         if (typeof(this.after) == 'object') {
9009             this.after.render(this.el.select('.roo-input-after',true).first());
9010         }
9011         
9012         
9013     },
9014     filterValidation : function(e){
9015         if(!e.isNavKeyPress()){
9016             this.validationTask.delay(this.validationDelay);
9017         }
9018     },
9019      /**
9020      * Validates the field value
9021      * @return {Boolean} True if the value is valid, else false
9022      */
9023     validate : function(){
9024         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9025         if(this.disabled || this.validateValue(this.getRawValue())){
9026             this.markValid();
9027             return true;
9028         }
9029         
9030         this.markInvalid();
9031         return false;
9032     },
9033     
9034     
9035     /**
9036      * Validates a value according to the field's validation rules and marks the field as invalid
9037      * if the validation fails
9038      * @param {Mixed} value The value to validate
9039      * @return {Boolean} True if the value is valid, else false
9040      */
9041     validateValue : function(value){
9042         if(value.length < 1)  { // if it's blank
9043             if(this.allowBlank){
9044                 return true;
9045             }            
9046             return this.inputEl().hasClass('hide') ? true : false;
9047         }
9048         
9049         if(value.length < this.minLength){
9050             return false;
9051         }
9052         if(value.length > this.maxLength){
9053             return false;
9054         }
9055         if(this.vtype){
9056             var vt = Roo.form.VTypes;
9057             if(!vt[this.vtype](value, this)){
9058                 return false;
9059             }
9060         }
9061         if(typeof this.validator == "function"){
9062             var msg = this.validator(value);
9063             if(msg !== true){
9064                 return false;
9065             }
9066             if (typeof(msg) == 'string') {
9067                 this.invalidText = msg;
9068             }
9069         }
9070         
9071         if(this.regex && !this.regex.test(value)){
9072             return false;
9073         }
9074         
9075         return true;
9076     },
9077
9078     
9079     
9080      // private
9081     fireKey : function(e){
9082         //Roo.log('field ' + e.getKey());
9083         if(e.isNavKeyPress()){
9084             this.fireEvent("specialkey", this, e);
9085         }
9086     },
9087     focus : function (selectText){
9088         if(this.rendered){
9089             this.inputEl().focus();
9090             if(selectText === true){
9091                 this.inputEl().dom.select();
9092             }
9093         }
9094         return this;
9095     } ,
9096     
9097     onFocus : function(){
9098         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9099            // this.el.addClass(this.focusClass);
9100         }
9101         if(!this.hasFocus){
9102             this.hasFocus = true;
9103             this.startValue = this.getValue();
9104             this.fireEvent("focus", this);
9105         }
9106     },
9107     
9108     beforeBlur : Roo.emptyFn,
9109
9110     
9111     // private
9112     onBlur : function(){
9113         this.beforeBlur();
9114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9115             //this.el.removeClass(this.focusClass);
9116         }
9117         this.hasFocus = false;
9118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9119             this.validate();
9120         }
9121         var v = this.getValue();
9122         if(String(v) !== String(this.startValue)){
9123             this.fireEvent('change', this, v, this.startValue);
9124         }
9125         this.fireEvent("blur", this);
9126     },
9127     
9128     /**
9129      * Resets the current field value to the originally loaded value and clears any validation messages
9130      */
9131     reset : function(){
9132         this.setValue(this.originalValue);
9133         this.validate();
9134     },
9135      /**
9136      * Returns the name of the field
9137      * @return {Mixed} name The name field
9138      */
9139     getName: function(){
9140         return this.name;
9141     },
9142      /**
9143      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9144      * @return {Mixed} value The field value
9145      */
9146     getValue : function(){
9147         
9148         var v = this.inputEl().getValue();
9149         
9150         return v;
9151     },
9152     /**
9153      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9154      * @return {Mixed} value The field value
9155      */
9156     getRawValue : function(){
9157         var v = this.inputEl().getValue();
9158         
9159         return v;
9160     },
9161     
9162     /**
9163      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9164      * @param {Mixed} value The value to set
9165      */
9166     setRawValue : function(v){
9167         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9168     },
9169     
9170     selectText : function(start, end){
9171         var v = this.getRawValue();
9172         if(v.length > 0){
9173             start = start === undefined ? 0 : start;
9174             end = end === undefined ? v.length : end;
9175             var d = this.inputEl().dom;
9176             if(d.setSelectionRange){
9177                 d.setSelectionRange(start, end);
9178             }else if(d.createTextRange){
9179                 var range = d.createTextRange();
9180                 range.moveStart("character", start);
9181                 range.moveEnd("character", v.length-end);
9182                 range.select();
9183             }
9184         }
9185     },
9186     
9187     /**
9188      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9189      * @param {Mixed} value The value to set
9190      */
9191     setValue : function(v){
9192         this.value = v;
9193         if(this.rendered){
9194             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9195             this.validate();
9196         }
9197     },
9198     
9199     /*
9200     processValue : function(value){
9201         if(this.stripCharsRe){
9202             var newValue = value.replace(this.stripCharsRe, '');
9203             if(newValue !== value){
9204                 this.setRawValue(newValue);
9205                 return newValue;
9206             }
9207         }
9208         return value;
9209     },
9210   */
9211     preFocus : function(){
9212         
9213         if(this.selectOnFocus){
9214             this.inputEl().dom.select();
9215         }
9216     },
9217     filterKeys : function(e){
9218         var k = e.getKey();
9219         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9220             return;
9221         }
9222         var c = e.getCharCode(), cc = String.fromCharCode(c);
9223         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9224             return;
9225         }
9226         if(!this.maskRe.test(cc)){
9227             e.stopEvent();
9228         }
9229     },
9230      /**
9231      * Clear any invalid styles/messages for this field
9232      */
9233     clearInvalid : function(){
9234         
9235         if(!this.el || this.preventMark){ // not rendered
9236             return;
9237         }
9238         
9239      
9240         this.el.removeClass(this.invalidClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9248             }
9249             
9250         }
9251         
9252         this.fireEvent('valid', this);
9253     },
9254     
9255      /**
9256      * Mark this field as valid
9257      */
9258     markValid : function()
9259     {
9260         if(!this.el  || this.preventMark){ // not rendered...
9261             return;
9262         }
9263         
9264         this.el.removeClass([this.invalidClass, this.validClass]);
9265         
9266         var feedback = this.el.select('.form-control-feedback', true).first();
9267             
9268         if(feedback){
9269             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9270         }
9271         
9272         if(this.indicator){
9273             this.indicator.removeClass('visible');
9274             this.indicator.addClass('invisible');
9275         }
9276         
9277         if(this.disabled){
9278             return;
9279         }
9280         
9281         if(this.allowBlank && !this.getRawValue().length){
9282             return;
9283         }
9284         
9285         this.el.addClass(this.validClass);
9286         
9287         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9288             
9289             var feedback = this.el.select('.form-control-feedback', true).first();
9290             
9291             if(feedback){
9292                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9293                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9294             }
9295             
9296         }
9297         
9298         this.fireEvent('valid', this);
9299     },
9300     
9301      /**
9302      * Mark this field as invalid
9303      * @param {String} msg The validation message
9304      */
9305     markInvalid : function(msg)
9306     {
9307         if(!this.el  || this.preventMark){ // not rendered
9308             return;
9309         }
9310         
9311         this.el.removeClass([this.invalidClass, this.validClass]);
9312         
9313         var feedback = this.el.select('.form-control-feedback', true).first();
9314             
9315         if(feedback){
9316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9317         }
9318
9319         if(this.disabled){
9320             return;
9321         }
9322         
9323         if(this.allowBlank && !this.getRawValue().length){
9324             return;
9325         }
9326         
9327         if(this.indicator){
9328             this.indicator.removeClass('invisible');
9329             this.indicator.addClass('visible');
9330         }
9331         
9332         this.el.addClass(this.invalidClass);
9333         
9334         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9335             
9336             var feedback = this.el.select('.form-control-feedback', true).first();
9337             
9338             if(feedback){
9339                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9340                 
9341                 if(this.getValue().length || this.forceFeedback){
9342                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9343                 }
9344                 
9345             }
9346             
9347         }
9348         
9349         this.fireEvent('invalid', this, msg);
9350     },
9351     // private
9352     SafariOnKeyDown : function(event)
9353     {
9354         // this is a workaround for a password hang bug on chrome/ webkit.
9355         if (this.inputEl().dom.type != 'password') {
9356             return;
9357         }
9358         
9359         var isSelectAll = false;
9360         
9361         if(this.inputEl().dom.selectionEnd > 0){
9362             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9363         }
9364         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9365             event.preventDefault();
9366             this.setValue('');
9367             return;
9368         }
9369         
9370         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9371             
9372             event.preventDefault();
9373             // this is very hacky as keydown always get's upper case.
9374             //
9375             var cc = String.fromCharCode(event.getCharCode());
9376             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9377             
9378         }
9379     },
9380     adjustWidth : function(tag, w){
9381         tag = tag.toLowerCase();
9382         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9383             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9384                 if(tag == 'input'){
9385                     return w + 2;
9386                 }
9387                 if(tag == 'textarea'){
9388                     return w-2;
9389                 }
9390             }else if(Roo.isOpera){
9391                 if(tag == 'input'){
9392                     return w + 2;
9393                 }
9394                 if(tag == 'textarea'){
9395                     return w-2;
9396                 }
9397             }
9398         }
9399         return w;
9400     },
9401     
9402     setFieldLabel : function(v)
9403     {
9404         if(!this.rendered){
9405             return;
9406         }
9407         
9408         if(this.indicator){
9409             var ar = this.el.select('label > span',true);
9410             
9411             if (ar.elements.length) {
9412                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9413                 this.fieldLabel = v;
9414                 return;
9415             }
9416             
9417             var br = this.el.select('label',true);
9418             
9419             if(br.elements.length) {
9420                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9421                 this.fieldLabel = v;
9422                 return;
9423             }
9424             
9425             Roo.log('Cannot Found any of label > span || label in input');
9426             return;
9427         }
9428         
9429         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9430         this.fieldLabel = v;
9431         
9432         
9433     }
9434 });
9435
9436  
9437 /*
9438  * - LGPL
9439  *
9440  * Input
9441  * 
9442  */
9443
9444 /**
9445  * @class Roo.bootstrap.TextArea
9446  * @extends Roo.bootstrap.Input
9447  * Bootstrap TextArea class
9448  * @cfg {Number} cols Specifies the visible width of a text area
9449  * @cfg {Number} rows Specifies the visible number of lines in a text area
9450  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9451  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9452  * @cfg {string} html text
9453  * 
9454  * @constructor
9455  * Create a new TextArea
9456  * @param {Object} config The config object
9457  */
9458
9459 Roo.bootstrap.TextArea = function(config){
9460     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9461    
9462 };
9463
9464 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9465      
9466     cols : false,
9467     rows : 5,
9468     readOnly : false,
9469     warp : 'soft',
9470     resize : false,
9471     value: false,
9472     html: false,
9473     
9474     getAutoCreate : function(){
9475         
9476         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9477         
9478         var id = Roo.id();
9479         
9480         var cfg = {};
9481         
9482         if(this.inputType != 'hidden'){
9483             cfg.cls = 'form-group' //input-group
9484         }
9485         
9486         var input =  {
9487             tag: 'textarea',
9488             id : id,
9489             warp : this.warp,
9490             rows : this.rows,
9491             value : this.value || '',
9492             html: this.html || '',
9493             cls : 'form-control',
9494             placeholder : this.placeholder || '' 
9495             
9496         };
9497         
9498         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9499             input.maxLength = this.maxLength;
9500         }
9501         
9502         if(this.resize){
9503             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9504         }
9505         
9506         if(this.cols){
9507             input.cols = this.cols;
9508         }
9509         
9510         if (this.readOnly) {
9511             input.readonly = true;
9512         }
9513         
9514         if (this.name) {
9515             input.name = this.name;
9516         }
9517         
9518         if (this.size) {
9519             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9520         }
9521         
9522         var settings=this;
9523         ['xs','sm','md','lg'].map(function(size){
9524             if (settings[size]) {
9525                 cfg.cls += ' col-' + size + '-' + settings[size];
9526             }
9527         });
9528         
9529         var inputblock = input;
9530         
9531         if(this.hasFeedback && !this.allowBlank){
9532             
9533             var feedback = {
9534                 tag: 'span',
9535                 cls: 'glyphicon form-control-feedback'
9536             };
9537
9538             inputblock = {
9539                 cls : 'has-feedback',
9540                 cn :  [
9541                     input,
9542                     feedback
9543                 ] 
9544             };  
9545         }
9546         
9547         
9548         if (this.before || this.after) {
9549             
9550             inputblock = {
9551                 cls : 'input-group',
9552                 cn :  [] 
9553             };
9554             if (this.before) {
9555                 inputblock.cn.push({
9556                     tag :'span',
9557                     cls : 'input-group-addon',
9558                     html : this.before
9559                 });
9560             }
9561             
9562             inputblock.cn.push(input);
9563             
9564             if(this.hasFeedback && !this.allowBlank){
9565                 inputblock.cls += ' has-feedback';
9566                 inputblock.cn.push(feedback);
9567             }
9568             
9569             if (this.after) {
9570                 inputblock.cn.push({
9571                     tag :'span',
9572                     cls : 'input-group-addon',
9573                     html : this.after
9574                 });
9575             }
9576             
9577         }
9578         
9579         if (align ==='left' && this.fieldLabel.length) {
9580             cfg.cn = [
9581                 {
9582                     tag: 'label',
9583                     'for' :  id,
9584                     cls : 'control-label',
9585                     html : this.fieldLabel
9586                 },
9587                 {
9588                     cls : "",
9589                     cn: [
9590                         inputblock
9591                     ]
9592                 }
9593
9594             ];
9595             
9596             if(this.labelWidth > 12){
9597                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9598             }
9599
9600             if(this.labelWidth < 13 && this.labelmd == 0){
9601                 this.labelmd = this.labelWidth;
9602             }
9603
9604             if(this.labellg > 0){
9605                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9606                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9607             }
9608
9609             if(this.labelmd > 0){
9610                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9611                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9612             }
9613
9614             if(this.labelsm > 0){
9615                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9616                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9617             }
9618
9619             if(this.labelxs > 0){
9620                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9621                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9622             }
9623             
9624         } else if ( this.fieldLabel.length) {
9625             cfg.cn = [
9626
9627                {
9628                    tag: 'label',
9629                    //cls : 'input-group-addon',
9630                    html : this.fieldLabel
9631
9632                },
9633
9634                inputblock
9635
9636            ];
9637
9638         } else {
9639
9640             cfg.cn = [
9641
9642                 inputblock
9643
9644             ];
9645                 
9646         }
9647         
9648         if (this.disabled) {
9649             input.disabled=true;
9650         }
9651         
9652         return cfg;
9653         
9654     },
9655     /**
9656      * return the real textarea element.
9657      */
9658     inputEl: function ()
9659     {
9660         return this.el.select('textarea.form-control',true).first();
9661     },
9662     
9663     /**
9664      * Clear any invalid styles/messages for this field
9665      */
9666     clearInvalid : function()
9667     {
9668         
9669         if(!this.el || this.preventMark){ // not rendered
9670             return;
9671         }
9672         
9673         var label = this.el.select('label', true).first();
9674         var icon = this.el.select('i.fa-star', true).first();
9675         
9676         if(label && icon){
9677             icon.remove();
9678         }
9679         
9680         this.el.removeClass(this.invalidClass);
9681         
9682         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9683             
9684             var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686             if(feedback){
9687                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9688             }
9689             
9690         }
9691         
9692         this.fireEvent('valid', this);
9693     },
9694     
9695      /**
9696      * Mark this field as valid
9697      */
9698     markValid : function()
9699     {
9700         if(!this.el  || this.preventMark){ // not rendered
9701             return;
9702         }
9703         
9704         this.el.removeClass([this.invalidClass, this.validClass]);
9705         
9706         var feedback = this.el.select('.form-control-feedback', true).first();
9707             
9708         if(feedback){
9709             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9710         }
9711
9712         if(this.disabled || this.allowBlank){
9713             return;
9714         }
9715         
9716         var label = this.el.select('label', true).first();
9717         var icon = this.el.select('i.fa-star', true).first();
9718         
9719         if(label && icon){
9720             icon.remove();
9721         }
9722         
9723         this.el.addClass(this.validClass);
9724         
9725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9726             
9727             var feedback = this.el.select('.form-control-feedback', true).first();
9728             
9729             if(feedback){
9730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9731                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9732             }
9733             
9734         }
9735         
9736         this.fireEvent('valid', this);
9737     },
9738     
9739      /**
9740      * Mark this field as invalid
9741      * @param {String} msg The validation message
9742      */
9743     markInvalid : function(msg)
9744     {
9745         if(!this.el  || this.preventMark){ // not rendered
9746             return;
9747         }
9748         
9749         this.el.removeClass([this.invalidClass, this.validClass]);
9750         
9751         var feedback = this.el.select('.form-control-feedback', true).first();
9752             
9753         if(feedback){
9754             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9755         }
9756
9757         if(this.disabled || this.allowBlank){
9758             return;
9759         }
9760         
9761         var label = this.el.select('label', true).first();
9762         var icon = this.el.select('i.fa-star', true).first();
9763         
9764         if(!this.getValue().length && label && !icon){
9765             this.el.createChild({
9766                 tag : 'i',
9767                 cls : 'text-danger fa fa-lg fa-star',
9768                 tooltip : 'This field is required',
9769                 style : 'margin-right:5px;'
9770             }, label, true);
9771         }
9772
9773         this.el.addClass(this.invalidClass);
9774         
9775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9776             
9777             var feedback = this.el.select('.form-control-feedback', true).first();
9778             
9779             if(feedback){
9780                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9781                 
9782                 if(this.getValue().length || this.forceFeedback){
9783                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9784                 }
9785                 
9786             }
9787             
9788         }
9789         
9790         this.fireEvent('invalid', this, msg);
9791     }
9792 });
9793
9794  
9795 /*
9796  * - LGPL
9797  *
9798  * trigger field - base class for combo..
9799  * 
9800  */
9801  
9802 /**
9803  * @class Roo.bootstrap.TriggerField
9804  * @extends Roo.bootstrap.Input
9805  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9806  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9807  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9808  * for which you can provide a custom implementation.  For example:
9809  * <pre><code>
9810 var trigger = new Roo.bootstrap.TriggerField();
9811 trigger.onTriggerClick = myTriggerFn;
9812 trigger.applyTo('my-field');
9813 </code></pre>
9814  *
9815  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9816  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9817  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9818  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9819  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9820
9821  * @constructor
9822  * Create a new TriggerField.
9823  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9824  * to the base TextField)
9825  */
9826 Roo.bootstrap.TriggerField = function(config){
9827     this.mimicing = false;
9828     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9829 };
9830
9831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9832     /**
9833      * @cfg {String} triggerClass A CSS class to apply to the trigger
9834      */
9835      /**
9836      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9837      */
9838     hideTrigger:false,
9839
9840     /**
9841      * @cfg {Boolean} removable (true|false) special filter default false
9842      */
9843     removable : false,
9844     
9845     /** @cfg {Boolean} grow @hide */
9846     /** @cfg {Number} growMin @hide */
9847     /** @cfg {Number} growMax @hide */
9848
9849     /**
9850      * @hide 
9851      * @method
9852      */
9853     autoSize: Roo.emptyFn,
9854     // private
9855     monitorTab : true,
9856     // private
9857     deferHeight : true,
9858
9859     
9860     actionMode : 'wrap',
9861     
9862     caret : false,
9863     
9864     
9865     getAutoCreate : function(){
9866        
9867         var align = this.labelAlign || this.parentLabelAlign();
9868         
9869         var id = Roo.id();
9870         
9871         var cfg = {
9872             cls: 'form-group' //input-group
9873         };
9874         
9875         
9876         var input =  {
9877             tag: 'input',
9878             id : id,
9879             type : this.inputType,
9880             cls : 'form-control',
9881             autocomplete: 'new-password',
9882             placeholder : this.placeholder || '' 
9883             
9884         };
9885         if (this.name) {
9886             input.name = this.name;
9887         }
9888         if (this.size) {
9889             input.cls += ' input-' + this.size;
9890         }
9891         
9892         if (this.disabled) {
9893             input.disabled=true;
9894         }
9895         
9896         var inputblock = input;
9897         
9898         if(this.hasFeedback && !this.allowBlank){
9899             
9900             var feedback = {
9901                 tag: 'span',
9902                 cls: 'glyphicon form-control-feedback'
9903             };
9904             
9905             if(this.removable && !this.editable && !this.tickable){
9906                 inputblock = {
9907                     cls : 'has-feedback',
9908                     cn :  [
9909                         inputblock,
9910                         {
9911                             tag: 'button',
9912                             html : 'x',
9913                             cls : 'roo-combo-removable-btn close'
9914                         },
9915                         feedback
9916                     ] 
9917                 };
9918             } else {
9919                 inputblock = {
9920                     cls : 'has-feedback',
9921                     cn :  [
9922                         inputblock,
9923                         feedback
9924                     ] 
9925                 };
9926             }
9927
9928         } else {
9929             if(this.removable && !this.editable && !this.tickable){
9930                 inputblock = {
9931                     cls : 'roo-removable',
9932                     cn :  [
9933                         inputblock,
9934                         {
9935                             tag: 'button',
9936                             html : 'x',
9937                             cls : 'roo-combo-removable-btn close'
9938                         }
9939                     ] 
9940                 };
9941             }
9942         }
9943         
9944         if (this.before || this.after) {
9945             
9946             inputblock = {
9947                 cls : 'input-group',
9948                 cn :  [] 
9949             };
9950             if (this.before) {
9951                 inputblock.cn.push({
9952                     tag :'span',
9953                     cls : 'input-group-addon',
9954                     html : this.before
9955                 });
9956             }
9957             
9958             inputblock.cn.push(input);
9959             
9960             if(this.hasFeedback && !this.allowBlank){
9961                 inputblock.cls += ' has-feedback';
9962                 inputblock.cn.push(feedback);
9963             }
9964             
9965             if (this.after) {
9966                 inputblock.cn.push({
9967                     tag :'span',
9968                     cls : 'input-group-addon',
9969                     html : this.after
9970                 });
9971             }
9972             
9973         };
9974         
9975         var box = {
9976             tag: 'div',
9977             cn: [
9978                 {
9979                     tag: 'input',
9980                     type : 'hidden',
9981                     cls: 'form-hidden-field'
9982                 },
9983                 inputblock
9984             ]
9985             
9986         };
9987         
9988         if(this.multiple){
9989             box = {
9990                 tag: 'div',
9991                 cn: [
9992                     {
9993                         tag: 'input',
9994                         type : 'hidden',
9995                         cls: 'form-hidden-field'
9996                     },
9997                     {
9998                         tag: 'ul',
9999                         cls: 'roo-select2-choices',
10000                         cn:[
10001                             {
10002                                 tag: 'li',
10003                                 cls: 'roo-select2-search-field',
10004                                 cn: [
10005
10006                                     inputblock
10007                                 ]
10008                             }
10009                         ]
10010                     }
10011                 ]
10012             }
10013         };
10014         
10015         var combobox = {
10016             cls: 'roo-select2-container input-group',
10017             cn: [
10018                 box
10019 //                {
10020 //                    tag: 'ul',
10021 //                    cls: 'typeahead typeahead-long dropdown-menu',
10022 //                    style: 'display:none'
10023 //                }
10024             ]
10025         };
10026         
10027         if(!this.multiple && this.showToggleBtn){
10028             
10029             var caret = {
10030                         tag: 'span',
10031                         cls: 'caret'
10032              };
10033             if (this.caret != false) {
10034                 caret = {
10035                      tag: 'i',
10036                      cls: 'fa fa-' + this.caret
10037                 };
10038                 
10039             }
10040             
10041             combobox.cn.push({
10042                 tag :'span',
10043                 cls : 'input-group-addon btn dropdown-toggle',
10044                 cn : [
10045                     caret,
10046                     {
10047                         tag: 'span',
10048                         cls: 'combobox-clear',
10049                         cn  : [
10050                             {
10051                                 tag : 'i',
10052                                 cls: 'icon-remove'
10053                             }
10054                         ]
10055                     }
10056                 ]
10057
10058             })
10059         }
10060         
10061         if(this.multiple){
10062             combobox.cls += ' roo-select2-container-multi';
10063         }
10064         
10065         if (align ==='left' && this.fieldLabel.length) {
10066             
10067             cfg.cls += ' roo-form-group-label-left';
10068
10069             cfg.cn = [
10070                 {
10071                     tag : 'i',
10072                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10073                     tooltip : 'This field is required'
10074                 },
10075                 {
10076                     tag: 'label',
10077                     'for' :  id,
10078                     cls : 'control-label',
10079                     html : this.fieldLabel
10080
10081                 },
10082                 {
10083                     cls : "", 
10084                     cn: [
10085                         combobox
10086                     ]
10087                 }
10088
10089             ];
10090             
10091             var labelCfg = cfg.cn[1];
10092             var contentCfg = cfg.cn[2];
10093             
10094             if(this.indicatorpos == 'right'){
10095                 cfg.cn = [
10096                     {
10097                         tag: 'label',
10098                         'for' :  id,
10099                         cls : 'control-label',
10100                         cn : [
10101                             {
10102                                 tag : 'span',
10103                                 html : this.fieldLabel
10104                             },
10105                             {
10106                                 tag : 'i',
10107                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10108                                 tooltip : 'This field is required'
10109                             }
10110                         ]
10111                     },
10112                     {
10113                         cls : "", 
10114                         cn: [
10115                             combobox
10116                         ]
10117                     }
10118
10119                 ];
10120                 
10121                 labelCfg = cfg.cn[0];
10122                 contentCfg = cfg.cn[1];
10123             }
10124             
10125             if(this.labelWidth > 12){
10126                 labelCfg.style = "width: " + this.labelWidth + 'px';
10127             }
10128             
10129             if(this.labelWidth < 13 && this.labelmd == 0){
10130                 this.labelmd = this.labelWidth;
10131             }
10132             
10133             if(this.labellg > 0){
10134                 labelCfg.cls += ' col-lg-' + this.labellg;
10135                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10136             }
10137             
10138             if(this.labelmd > 0){
10139                 labelCfg.cls += ' col-md-' + this.labelmd;
10140                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10141             }
10142             
10143             if(this.labelsm > 0){
10144                 labelCfg.cls += ' col-sm-' + this.labelsm;
10145                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10146             }
10147             
10148             if(this.labelxs > 0){
10149                 labelCfg.cls += ' col-xs-' + this.labelxs;
10150                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10151             }
10152             
10153         } else if ( this.fieldLabel.length) {
10154 //                Roo.log(" label");
10155             cfg.cn = [
10156                 {
10157                    tag : 'i',
10158                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10159                    tooltip : 'This field is required'
10160                },
10161                {
10162                    tag: 'label',
10163                    //cls : 'input-group-addon',
10164                    html : this.fieldLabel
10165
10166                },
10167
10168                combobox
10169
10170             ];
10171             
10172             if(this.indicatorpos == 'right'){
10173                 
10174                 cfg.cn = [
10175                     {
10176                        tag: 'label',
10177                        cn : [
10178                            {
10179                                tag : 'span',
10180                                html : this.fieldLabel
10181                            },
10182                            {
10183                               tag : 'i',
10184                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10185                               tooltip : 'This field is required'
10186                            }
10187                        ]
10188
10189                     },
10190                     combobox
10191
10192                 ];
10193
10194             }
10195
10196         } else {
10197             
10198 //                Roo.log(" no label && no align");
10199                 cfg = combobox
10200                      
10201                 
10202         }
10203         
10204         var settings=this;
10205         ['xs','sm','md','lg'].map(function(size){
10206             if (settings[size]) {
10207                 cfg.cls += ' col-' + size + '-' + settings[size];
10208             }
10209         });
10210         
10211         return cfg;
10212         
10213     },
10214     
10215     
10216     
10217     // private
10218     onResize : function(w, h){
10219 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10220 //        if(typeof w == 'number'){
10221 //            var x = w - this.trigger.getWidth();
10222 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10223 //            this.trigger.setStyle('left', x+'px');
10224 //        }
10225     },
10226
10227     // private
10228     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10229
10230     // private
10231     getResizeEl : function(){
10232         return this.inputEl();
10233     },
10234
10235     // private
10236     getPositionEl : function(){
10237         return this.inputEl();
10238     },
10239
10240     // private
10241     alignErrorIcon : function(){
10242         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10243     },
10244
10245     // private
10246     initEvents : function(){
10247         
10248         this.createList();
10249         
10250         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10251         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10252         if(!this.multiple && this.showToggleBtn){
10253             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10254             if(this.hideTrigger){
10255                 this.trigger.setDisplayed(false);
10256             }
10257             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10258         }
10259         
10260         if(this.multiple){
10261             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10262         }
10263         
10264         if(this.removable && !this.editable && !this.tickable){
10265             var close = this.closeTriggerEl();
10266             
10267             if(close){
10268                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10269                 close.on('click', this.removeBtnClick, this, close);
10270             }
10271         }
10272         
10273         //this.trigger.addClassOnOver('x-form-trigger-over');
10274         //this.trigger.addClassOnClick('x-form-trigger-click');
10275         
10276         //if(!this.width){
10277         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10278         //}
10279     },
10280     
10281     closeTriggerEl : function()
10282     {
10283         var close = this.el.select('.roo-combo-removable-btn', true).first();
10284         return close ? close : false;
10285     },
10286     
10287     removeBtnClick : function(e, h, el)
10288     {
10289         e.preventDefault();
10290         
10291         if(this.fireEvent("remove", this) !== false){
10292             this.reset();
10293             this.fireEvent("afterremove", this)
10294         }
10295     },
10296     
10297     createList : function()
10298     {
10299         this.list = Roo.get(document.body).createChild({
10300             tag: 'ul',
10301             cls: 'typeahead typeahead-long dropdown-menu',
10302             style: 'display:none'
10303         });
10304         
10305         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10306         
10307     },
10308
10309     // private
10310     initTrigger : function(){
10311        
10312     },
10313
10314     // private
10315     onDestroy : function(){
10316         if(this.trigger){
10317             this.trigger.removeAllListeners();
10318           //  this.trigger.remove();
10319         }
10320         //if(this.wrap){
10321         //    this.wrap.remove();
10322         //}
10323         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10324     },
10325
10326     // private
10327     onFocus : function(){
10328         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10329         /*
10330         if(!this.mimicing){
10331             this.wrap.addClass('x-trigger-wrap-focus');
10332             this.mimicing = true;
10333             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10334             if(this.monitorTab){
10335                 this.el.on("keydown", this.checkTab, this);
10336             }
10337         }
10338         */
10339     },
10340
10341     // private
10342     checkTab : function(e){
10343         if(e.getKey() == e.TAB){
10344             this.triggerBlur();
10345         }
10346     },
10347
10348     // private
10349     onBlur : function(){
10350         // do nothing
10351     },
10352
10353     // private
10354     mimicBlur : function(e, t){
10355         /*
10356         if(!this.wrap.contains(t) && this.validateBlur()){
10357             this.triggerBlur();
10358         }
10359         */
10360     },
10361
10362     // private
10363     triggerBlur : function(){
10364         this.mimicing = false;
10365         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10366         if(this.monitorTab){
10367             this.el.un("keydown", this.checkTab, this);
10368         }
10369         //this.wrap.removeClass('x-trigger-wrap-focus');
10370         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10371     },
10372
10373     // private
10374     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10375     validateBlur : function(e, t){
10376         return true;
10377     },
10378
10379     // private
10380     onDisable : function(){
10381         this.inputEl().dom.disabled = true;
10382         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10383         //if(this.wrap){
10384         //    this.wrap.addClass('x-item-disabled');
10385         //}
10386     },
10387
10388     // private
10389     onEnable : function(){
10390         this.inputEl().dom.disabled = false;
10391         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10392         //if(this.wrap){
10393         //    this.el.removeClass('x-item-disabled');
10394         //}
10395     },
10396
10397     // private
10398     onShow : function(){
10399         var ae = this.getActionEl();
10400         
10401         if(ae){
10402             ae.dom.style.display = '';
10403             ae.dom.style.visibility = 'visible';
10404         }
10405     },
10406
10407     // private
10408     
10409     onHide : function(){
10410         var ae = this.getActionEl();
10411         ae.dom.style.display = 'none';
10412     },
10413
10414     /**
10415      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10416      * by an implementing function.
10417      * @method
10418      * @param {EventObject} e
10419      */
10420     onTriggerClick : Roo.emptyFn
10421 });
10422  /*
10423  * Based on:
10424  * Ext JS Library 1.1.1
10425  * Copyright(c) 2006-2007, Ext JS, LLC.
10426  *
10427  * Originally Released Under LGPL - original licence link has changed is not relivant.
10428  *
10429  * Fork - LGPL
10430  * <script type="text/javascript">
10431  */
10432
10433
10434 /**
10435  * @class Roo.data.SortTypes
10436  * @singleton
10437  * Defines the default sorting (casting?) comparison functions used when sorting data.
10438  */
10439 Roo.data.SortTypes = {
10440     /**
10441      * Default sort that does nothing
10442      * @param {Mixed} s The value being converted
10443      * @return {Mixed} The comparison value
10444      */
10445     none : function(s){
10446         return s;
10447     },
10448     
10449     /**
10450      * The regular expression used to strip tags
10451      * @type {RegExp}
10452      * @property
10453      */
10454     stripTagsRE : /<\/?[^>]+>/gi,
10455     
10456     /**
10457      * Strips all HTML tags to sort on text only
10458      * @param {Mixed} s The value being converted
10459      * @return {String} The comparison value
10460      */
10461     asText : function(s){
10462         return String(s).replace(this.stripTagsRE, "");
10463     },
10464     
10465     /**
10466      * Strips all HTML tags to sort on text only - Case insensitive
10467      * @param {Mixed} s The value being converted
10468      * @return {String} The comparison value
10469      */
10470     asUCText : function(s){
10471         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10472     },
10473     
10474     /**
10475      * Case insensitive string
10476      * @param {Mixed} s The value being converted
10477      * @return {String} The comparison value
10478      */
10479     asUCString : function(s) {
10480         return String(s).toUpperCase();
10481     },
10482     
10483     /**
10484      * Date sorting
10485      * @param {Mixed} s The value being converted
10486      * @return {Number} The comparison value
10487      */
10488     asDate : function(s) {
10489         if(!s){
10490             return 0;
10491         }
10492         if(s instanceof Date){
10493             return s.getTime();
10494         }
10495         return Date.parse(String(s));
10496     },
10497     
10498     /**
10499      * Float sorting
10500      * @param {Mixed} s The value being converted
10501      * @return {Float} The comparison value
10502      */
10503     asFloat : function(s) {
10504         var val = parseFloat(String(s).replace(/,/g, ""));
10505         if(isNaN(val)) {
10506             val = 0;
10507         }
10508         return val;
10509     },
10510     
10511     /**
10512      * Integer sorting
10513      * @param {Mixed} s The value being converted
10514      * @return {Number} The comparison value
10515      */
10516     asInt : function(s) {
10517         var val = parseInt(String(s).replace(/,/g, ""));
10518         if(isNaN(val)) {
10519             val = 0;
10520         }
10521         return val;
10522     }
10523 };/*
10524  * Based on:
10525  * Ext JS Library 1.1.1
10526  * Copyright(c) 2006-2007, Ext JS, LLC.
10527  *
10528  * Originally Released Under LGPL - original licence link has changed is not relivant.
10529  *
10530  * Fork - LGPL
10531  * <script type="text/javascript">
10532  */
10533
10534 /**
10535 * @class Roo.data.Record
10536  * Instances of this class encapsulate both record <em>definition</em> information, and record
10537  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10538  * to access Records cached in an {@link Roo.data.Store} object.<br>
10539  * <p>
10540  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10541  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10542  * objects.<br>
10543  * <p>
10544  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10545  * @constructor
10546  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10547  * {@link #create}. The parameters are the same.
10548  * @param {Array} data An associative Array of data values keyed by the field name.
10549  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10550  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10551  * not specified an integer id is generated.
10552  */
10553 Roo.data.Record = function(data, id){
10554     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10555     this.data = data;
10556 };
10557
10558 /**
10559  * Generate a constructor for a specific record layout.
10560  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10561  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10562  * Each field definition object may contain the following properties: <ul>
10563  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10564  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10565  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10566  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10567  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10568  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10569  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10570  * this may be omitted.</p></li>
10571  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10572  * <ul><li>auto (Default, implies no conversion)</li>
10573  * <li>string</li>
10574  * <li>int</li>
10575  * <li>float</li>
10576  * <li>boolean</li>
10577  * <li>date</li></ul></p></li>
10578  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10579  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10580  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10581  * by the Reader into an object that will be stored in the Record. It is passed the
10582  * following parameters:<ul>
10583  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10584  * </ul></p></li>
10585  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10586  * </ul>
10587  * <br>usage:<br><pre><code>
10588 var TopicRecord = Roo.data.Record.create(
10589     {name: 'title', mapping: 'topic_title'},
10590     {name: 'author', mapping: 'username'},
10591     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10592     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10593     {name: 'lastPoster', mapping: 'user2'},
10594     {name: 'excerpt', mapping: 'post_text'}
10595 );
10596
10597 var myNewRecord = new TopicRecord({
10598     title: 'Do my job please',
10599     author: 'noobie',
10600     totalPosts: 1,
10601     lastPost: new Date(),
10602     lastPoster: 'Animal',
10603     excerpt: 'No way dude!'
10604 });
10605 myStore.add(myNewRecord);
10606 </code></pre>
10607  * @method create
10608  * @static
10609  */
10610 Roo.data.Record.create = function(o){
10611     var f = function(){
10612         f.superclass.constructor.apply(this, arguments);
10613     };
10614     Roo.extend(f, Roo.data.Record);
10615     var p = f.prototype;
10616     p.fields = new Roo.util.MixedCollection(false, function(field){
10617         return field.name;
10618     });
10619     for(var i = 0, len = o.length; i < len; i++){
10620         p.fields.add(new Roo.data.Field(o[i]));
10621     }
10622     f.getField = function(name){
10623         return p.fields.get(name);  
10624     };
10625     return f;
10626 };
10627
10628 Roo.data.Record.AUTO_ID = 1000;
10629 Roo.data.Record.EDIT = 'edit';
10630 Roo.data.Record.REJECT = 'reject';
10631 Roo.data.Record.COMMIT = 'commit';
10632
10633 Roo.data.Record.prototype = {
10634     /**
10635      * Readonly flag - true if this record has been modified.
10636      * @type Boolean
10637      */
10638     dirty : false,
10639     editing : false,
10640     error: null,
10641     modified: null,
10642
10643     // private
10644     join : function(store){
10645         this.store = store;
10646     },
10647
10648     /**
10649      * Set the named field to the specified value.
10650      * @param {String} name The name of the field to set.
10651      * @param {Object} value The value to set the field to.
10652      */
10653     set : function(name, value){
10654         if(this.data[name] == value){
10655             return;
10656         }
10657         this.dirty = true;
10658         if(!this.modified){
10659             this.modified = {};
10660         }
10661         if(typeof this.modified[name] == 'undefined'){
10662             this.modified[name] = this.data[name];
10663         }
10664         this.data[name] = value;
10665         if(!this.editing && this.store){
10666             this.store.afterEdit(this);
10667         }       
10668     },
10669
10670     /**
10671      * Get the value of the named field.
10672      * @param {String} name The name of the field to get the value of.
10673      * @return {Object} The value of the field.
10674      */
10675     get : function(name){
10676         return this.data[name]; 
10677     },
10678
10679     // private
10680     beginEdit : function(){
10681         this.editing = true;
10682         this.modified = {}; 
10683     },
10684
10685     // private
10686     cancelEdit : function(){
10687         this.editing = false;
10688         delete this.modified;
10689     },
10690
10691     // private
10692     endEdit : function(){
10693         this.editing = false;
10694         if(this.dirty && this.store){
10695             this.store.afterEdit(this);
10696         }
10697     },
10698
10699     /**
10700      * Usually called by the {@link Roo.data.Store} which owns the Record.
10701      * Rejects all changes made to the Record since either creation, or the last commit operation.
10702      * Modified fields are reverted to their original values.
10703      * <p>
10704      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10705      * of reject operations.
10706      */
10707     reject : function(){
10708         var m = this.modified;
10709         for(var n in m){
10710             if(typeof m[n] != "function"){
10711                 this.data[n] = m[n];
10712             }
10713         }
10714         this.dirty = false;
10715         delete this.modified;
10716         this.editing = false;
10717         if(this.store){
10718             this.store.afterReject(this);
10719         }
10720     },
10721
10722     /**
10723      * Usually called by the {@link Roo.data.Store} which owns the Record.
10724      * Commits all changes made to the Record since either creation, or the last commit operation.
10725      * <p>
10726      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10727      * of commit operations.
10728      */
10729     commit : function(){
10730         this.dirty = false;
10731         delete this.modified;
10732         this.editing = false;
10733         if(this.store){
10734             this.store.afterCommit(this);
10735         }
10736     },
10737
10738     // private
10739     hasError : function(){
10740         return this.error != null;
10741     },
10742
10743     // private
10744     clearError : function(){
10745         this.error = null;
10746     },
10747
10748     /**
10749      * Creates a copy of this record.
10750      * @param {String} id (optional) A new record id if you don't want to use this record's id
10751      * @return {Record}
10752      */
10753     copy : function(newId) {
10754         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10755     }
10756 };/*
10757  * Based on:
10758  * Ext JS Library 1.1.1
10759  * Copyright(c) 2006-2007, Ext JS, LLC.
10760  *
10761  * Originally Released Under LGPL - original licence link has changed is not relivant.
10762  *
10763  * Fork - LGPL
10764  * <script type="text/javascript">
10765  */
10766
10767
10768
10769 /**
10770  * @class Roo.data.Store
10771  * @extends Roo.util.Observable
10772  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10773  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10774  * <p>
10775  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10776  * has no knowledge of the format of the data returned by the Proxy.<br>
10777  * <p>
10778  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10779  * instances from the data object. These records are cached and made available through accessor functions.
10780  * @constructor
10781  * Creates a new Store.
10782  * @param {Object} config A config object containing the objects needed for the Store to access data,
10783  * and read the data into Records.
10784  */
10785 Roo.data.Store = function(config){
10786     this.data = new Roo.util.MixedCollection(false);
10787     this.data.getKey = function(o){
10788         return o.id;
10789     };
10790     this.baseParams = {};
10791     // private
10792     this.paramNames = {
10793         "start" : "start",
10794         "limit" : "limit",
10795         "sort" : "sort",
10796         "dir" : "dir",
10797         "multisort" : "_multisort"
10798     };
10799
10800     if(config && config.data){
10801         this.inlineData = config.data;
10802         delete config.data;
10803     }
10804
10805     Roo.apply(this, config);
10806     
10807     if(this.reader){ // reader passed
10808         this.reader = Roo.factory(this.reader, Roo.data);
10809         this.reader.xmodule = this.xmodule || false;
10810         if(!this.recordType){
10811             this.recordType = this.reader.recordType;
10812         }
10813         if(this.reader.onMetaChange){
10814             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10815         }
10816     }
10817
10818     if(this.recordType){
10819         this.fields = this.recordType.prototype.fields;
10820     }
10821     this.modified = [];
10822
10823     this.addEvents({
10824         /**
10825          * @event datachanged
10826          * Fires when the data cache has changed, and a widget which is using this Store
10827          * as a Record cache should refresh its view.
10828          * @param {Store} this
10829          */
10830         datachanged : true,
10831         /**
10832          * @event metachange
10833          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10834          * @param {Store} this
10835          * @param {Object} meta The JSON metadata
10836          */
10837         metachange : true,
10838         /**
10839          * @event add
10840          * Fires when Records have been added to the Store
10841          * @param {Store} this
10842          * @param {Roo.data.Record[]} records The array of Records added
10843          * @param {Number} index The index at which the record(s) were added
10844          */
10845         add : true,
10846         /**
10847          * @event remove
10848          * Fires when a Record has been removed from the Store
10849          * @param {Store} this
10850          * @param {Roo.data.Record} record The Record that was removed
10851          * @param {Number} index The index at which the record was removed
10852          */
10853         remove : true,
10854         /**
10855          * @event update
10856          * Fires when a Record has been updated
10857          * @param {Store} this
10858          * @param {Roo.data.Record} record The Record that was updated
10859          * @param {String} operation The update operation being performed.  Value may be one of:
10860          * <pre><code>
10861  Roo.data.Record.EDIT
10862  Roo.data.Record.REJECT
10863  Roo.data.Record.COMMIT
10864          * </code></pre>
10865          */
10866         update : true,
10867         /**
10868          * @event clear
10869          * Fires when the data cache has been cleared.
10870          * @param {Store} this
10871          */
10872         clear : true,
10873         /**
10874          * @event beforeload
10875          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10876          * the load action will be canceled.
10877          * @param {Store} this
10878          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10879          */
10880         beforeload : true,
10881         /**
10882          * @event beforeloadadd
10883          * Fires after a new set of Records has been loaded.
10884          * @param {Store} this
10885          * @param {Roo.data.Record[]} records The Records that were loaded
10886          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10887          */
10888         beforeloadadd : true,
10889         /**
10890          * @event load
10891          * Fires after a new set of Records has been loaded, before they are added to the store.
10892          * @param {Store} this
10893          * @param {Roo.data.Record[]} records The Records that were loaded
10894          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10895          * @params {Object} return from reader
10896          */
10897         load : true,
10898         /**
10899          * @event loadexception
10900          * Fires if an exception occurs in the Proxy during loading.
10901          * Called with the signature of the Proxy's "loadexception" event.
10902          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10903          * 
10904          * @param {Proxy} 
10905          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10906          * @param {Object} load options 
10907          * @param {Object} jsonData from your request (normally this contains the Exception)
10908          */
10909         loadexception : true
10910     });
10911     
10912     if(this.proxy){
10913         this.proxy = Roo.factory(this.proxy, Roo.data);
10914         this.proxy.xmodule = this.xmodule || false;
10915         this.relayEvents(this.proxy,  ["loadexception"]);
10916     }
10917     this.sortToggle = {};
10918     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10919
10920     Roo.data.Store.superclass.constructor.call(this);
10921
10922     if(this.inlineData){
10923         this.loadData(this.inlineData);
10924         delete this.inlineData;
10925     }
10926 };
10927
10928 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10929      /**
10930     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10931     * without a remote query - used by combo/forms at present.
10932     */
10933     
10934     /**
10935     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10936     */
10937     /**
10938     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10939     */
10940     /**
10941     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10942     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10943     */
10944     /**
10945     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10946     * on any HTTP request
10947     */
10948     /**
10949     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10950     */
10951     /**
10952     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10953     */
10954     multiSort: false,
10955     /**
10956     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10957     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10958     */
10959     remoteSort : false,
10960
10961     /**
10962     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10963      * loaded or when a record is removed. (defaults to false).
10964     */
10965     pruneModifiedRecords : false,
10966
10967     // private
10968     lastOptions : null,
10969
10970     /**
10971      * Add Records to the Store and fires the add event.
10972      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10973      */
10974     add : function(records){
10975         records = [].concat(records);
10976         for(var i = 0, len = records.length; i < len; i++){
10977             records[i].join(this);
10978         }
10979         var index = this.data.length;
10980         this.data.addAll(records);
10981         this.fireEvent("add", this, records, index);
10982     },
10983
10984     /**
10985      * Remove a Record from the Store and fires the remove event.
10986      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10987      */
10988     remove : function(record){
10989         var index = this.data.indexOf(record);
10990         this.data.removeAt(index);
10991         if(this.pruneModifiedRecords){
10992             this.modified.remove(record);
10993         }
10994         this.fireEvent("remove", this, record, index);
10995     },
10996
10997     /**
10998      * Remove all Records from the Store and fires the clear event.
10999      */
11000     removeAll : function(){
11001         this.data.clear();
11002         if(this.pruneModifiedRecords){
11003             this.modified = [];
11004         }
11005         this.fireEvent("clear", this);
11006     },
11007
11008     /**
11009      * Inserts Records to the Store at the given index and fires the add event.
11010      * @param {Number} index The start index at which to insert the passed Records.
11011      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11012      */
11013     insert : function(index, records){
11014         records = [].concat(records);
11015         for(var i = 0, len = records.length; i < len; i++){
11016             this.data.insert(index, records[i]);
11017             records[i].join(this);
11018         }
11019         this.fireEvent("add", this, records, index);
11020     },
11021
11022     /**
11023      * Get the index within the cache of the passed Record.
11024      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11025      * @return {Number} The index of the passed Record. Returns -1 if not found.
11026      */
11027     indexOf : function(record){
11028         return this.data.indexOf(record);
11029     },
11030
11031     /**
11032      * Get the index within the cache of the Record with the passed id.
11033      * @param {String} id The id of the Record to find.
11034      * @return {Number} The index of the Record. Returns -1 if not found.
11035      */
11036     indexOfId : function(id){
11037         return this.data.indexOfKey(id);
11038     },
11039
11040     /**
11041      * Get the Record with the specified id.
11042      * @param {String} id The id of the Record to find.
11043      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11044      */
11045     getById : function(id){
11046         return this.data.key(id);
11047     },
11048
11049     /**
11050      * Get the Record at the specified index.
11051      * @param {Number} index The index of the Record to find.
11052      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11053      */
11054     getAt : function(index){
11055         return this.data.itemAt(index);
11056     },
11057
11058     /**
11059      * Returns a range of Records between specified indices.
11060      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11061      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11062      * @return {Roo.data.Record[]} An array of Records
11063      */
11064     getRange : function(start, end){
11065         return this.data.getRange(start, end);
11066     },
11067
11068     // private
11069     storeOptions : function(o){
11070         o = Roo.apply({}, o);
11071         delete o.callback;
11072         delete o.scope;
11073         this.lastOptions = o;
11074     },
11075
11076     /**
11077      * Loads the Record cache from the configured Proxy using the configured Reader.
11078      * <p>
11079      * If using remote paging, then the first load call must specify the <em>start</em>
11080      * and <em>limit</em> properties in the options.params property to establish the initial
11081      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11082      * <p>
11083      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11084      * and this call will return before the new data has been loaded. Perform any post-processing
11085      * in a callback function, or in a "load" event handler.</strong>
11086      * <p>
11087      * @param {Object} options An object containing properties which control loading options:<ul>
11088      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11089      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11090      * passed the following arguments:<ul>
11091      * <li>r : Roo.data.Record[]</li>
11092      * <li>options: Options object from the load call</li>
11093      * <li>success: Boolean success indicator</li></ul></li>
11094      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11095      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11096      * </ul>
11097      */
11098     load : function(options){
11099         options = options || {};
11100         if(this.fireEvent("beforeload", this, options) !== false){
11101             this.storeOptions(options);
11102             var p = Roo.apply(options.params || {}, this.baseParams);
11103             // if meta was not loaded from remote source.. try requesting it.
11104             if (!this.reader.metaFromRemote) {
11105                 p._requestMeta = 1;
11106             }
11107             if(this.sortInfo && this.remoteSort){
11108                 var pn = this.paramNames;
11109                 p[pn["sort"]] = this.sortInfo.field;
11110                 p[pn["dir"]] = this.sortInfo.direction;
11111             }
11112             if (this.multiSort) {
11113                 var pn = this.paramNames;
11114                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11115             }
11116             
11117             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11118         }
11119     },
11120
11121     /**
11122      * Reloads the Record cache from the configured Proxy using the configured Reader and
11123      * the options from the last load operation performed.
11124      * @param {Object} options (optional) An object containing properties which may override the options
11125      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11126      * the most recently used options are reused).
11127      */
11128     reload : function(options){
11129         this.load(Roo.applyIf(options||{}, this.lastOptions));
11130     },
11131
11132     // private
11133     // Called as a callback by the Reader during a load operation.
11134     loadRecords : function(o, options, success){
11135         if(!o || success === false){
11136             if(success !== false){
11137                 this.fireEvent("load", this, [], options, o);
11138             }
11139             if(options.callback){
11140                 options.callback.call(options.scope || this, [], options, false);
11141             }
11142             return;
11143         }
11144         // if data returned failure - throw an exception.
11145         if (o.success === false) {
11146             // show a message if no listener is registered.
11147             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11148                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11149             }
11150             // loadmask wil be hooked into this..
11151             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11152             return;
11153         }
11154         var r = o.records, t = o.totalRecords || r.length;
11155         
11156         this.fireEvent("beforeloadadd", this, r, options, o);
11157         
11158         if(!options || options.add !== true){
11159             if(this.pruneModifiedRecords){
11160                 this.modified = [];
11161             }
11162             for(var i = 0, len = r.length; i < len; i++){
11163                 r[i].join(this);
11164             }
11165             if(this.snapshot){
11166                 this.data = this.snapshot;
11167                 delete this.snapshot;
11168             }
11169             this.data.clear();
11170             this.data.addAll(r);
11171             this.totalLength = t;
11172             this.applySort();
11173             this.fireEvent("datachanged", this);
11174         }else{
11175             this.totalLength = Math.max(t, this.data.length+r.length);
11176             this.add(r);
11177         }
11178         
11179         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11180                 
11181             var e = new Roo.data.Record({});
11182
11183             e.set(this.parent.displayField, this.parent.emptyTitle);
11184             e.set(this.parent.valueField, '');
11185
11186             this.insert(0, e);
11187         }
11188             
11189         this.fireEvent("load", this, r, options, o);
11190         if(options.callback){
11191             options.callback.call(options.scope || this, r, options, true);
11192         }
11193     },
11194
11195
11196     /**
11197      * Loads data from a passed data block. A Reader which understands the format of the data
11198      * must have been configured in the constructor.
11199      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11200      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11201      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11202      */
11203     loadData : function(o, append){
11204         var r = this.reader.readRecords(o);
11205         this.loadRecords(r, {add: append}, true);
11206     },
11207
11208     /**
11209      * Gets the number of cached records.
11210      * <p>
11211      * <em>If using paging, this may not be the total size of the dataset. If the data object
11212      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11213      * the data set size</em>
11214      */
11215     getCount : function(){
11216         return this.data.length || 0;
11217     },
11218
11219     /**
11220      * Gets the total number of records in the dataset as returned by the server.
11221      * <p>
11222      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11223      * the dataset size</em>
11224      */
11225     getTotalCount : function(){
11226         return this.totalLength || 0;
11227     },
11228
11229     /**
11230      * Returns the sort state of the Store as an object with two properties:
11231      * <pre><code>
11232  field {String} The name of the field by which the Records are sorted
11233  direction {String} The sort order, "ASC" or "DESC"
11234      * </code></pre>
11235      */
11236     getSortState : function(){
11237         return this.sortInfo;
11238     },
11239
11240     // private
11241     applySort : function(){
11242         if(this.sortInfo && !this.remoteSort){
11243             var s = this.sortInfo, f = s.field;
11244             var st = this.fields.get(f).sortType;
11245             var fn = function(r1, r2){
11246                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11247                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11248             };
11249             this.data.sort(s.direction, fn);
11250             if(this.snapshot && this.snapshot != this.data){
11251                 this.snapshot.sort(s.direction, fn);
11252             }
11253         }
11254     },
11255
11256     /**
11257      * Sets the default sort column and order to be used by the next load operation.
11258      * @param {String} fieldName The name of the field to sort by.
11259      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11260      */
11261     setDefaultSort : function(field, dir){
11262         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11263     },
11264
11265     /**
11266      * Sort the Records.
11267      * If remote sorting is used, the sort is performed on the server, and the cache is
11268      * reloaded. If local sorting is used, the cache is sorted internally.
11269      * @param {String} fieldName The name of the field to sort by.
11270      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11271      */
11272     sort : function(fieldName, dir){
11273         var f = this.fields.get(fieldName);
11274         if(!dir){
11275             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11276             
11277             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11278                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11279             }else{
11280                 dir = f.sortDir;
11281             }
11282         }
11283         this.sortToggle[f.name] = dir;
11284         this.sortInfo = {field: f.name, direction: dir};
11285         if(!this.remoteSort){
11286             this.applySort();
11287             this.fireEvent("datachanged", this);
11288         }else{
11289             this.load(this.lastOptions);
11290         }
11291     },
11292
11293     /**
11294      * Calls the specified function for each of the Records in the cache.
11295      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11296      * Returning <em>false</em> aborts and exits the iteration.
11297      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11298      */
11299     each : function(fn, scope){
11300         this.data.each(fn, scope);
11301     },
11302
11303     /**
11304      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11305      * (e.g., during paging).
11306      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11307      */
11308     getModifiedRecords : function(){
11309         return this.modified;
11310     },
11311
11312     // private
11313     createFilterFn : function(property, value, anyMatch){
11314         if(!value.exec){ // not a regex
11315             value = String(value);
11316             if(value.length == 0){
11317                 return false;
11318             }
11319             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11320         }
11321         return function(r){
11322             return value.test(r.data[property]);
11323         };
11324     },
11325
11326     /**
11327      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11328      * @param {String} property A field on your records
11329      * @param {Number} start The record index to start at (defaults to 0)
11330      * @param {Number} end The last record index to include (defaults to length - 1)
11331      * @return {Number} The sum
11332      */
11333     sum : function(property, start, end){
11334         var rs = this.data.items, v = 0;
11335         start = start || 0;
11336         end = (end || end === 0) ? end : rs.length-1;
11337
11338         for(var i = start; i <= end; i++){
11339             v += (rs[i].data[property] || 0);
11340         }
11341         return v;
11342     },
11343
11344     /**
11345      * Filter the records by a specified property.
11346      * @param {String} field A field on your records
11347      * @param {String/RegExp} value Either a string that the field
11348      * should start with or a RegExp to test against the field
11349      * @param {Boolean} anyMatch True to match any part not just the beginning
11350      */
11351     filter : function(property, value, anyMatch){
11352         var fn = this.createFilterFn(property, value, anyMatch);
11353         return fn ? this.filterBy(fn) : this.clearFilter();
11354     },
11355
11356     /**
11357      * Filter by a function. The specified function will be called with each
11358      * record in this data source. If the function returns true the record is included,
11359      * otherwise it is filtered.
11360      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11361      * @param {Object} scope (optional) The scope of the function (defaults to this)
11362      */
11363     filterBy : function(fn, scope){
11364         this.snapshot = this.snapshot || this.data;
11365         this.data = this.queryBy(fn, scope||this);
11366         this.fireEvent("datachanged", this);
11367     },
11368
11369     /**
11370      * Query the records by a specified property.
11371      * @param {String} field A field on your records
11372      * @param {String/RegExp} value Either a string that the field
11373      * should start with or a RegExp to test against the field
11374      * @param {Boolean} anyMatch True to match any part not just the beginning
11375      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11376      */
11377     query : function(property, value, anyMatch){
11378         var fn = this.createFilterFn(property, value, anyMatch);
11379         return fn ? this.queryBy(fn) : this.data.clone();
11380     },
11381
11382     /**
11383      * Query by a function. The specified function will be called with each
11384      * record in this data source. If the function returns true the record is included
11385      * in the results.
11386      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11387      * @param {Object} scope (optional) The scope of the function (defaults to this)
11388       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11389      **/
11390     queryBy : function(fn, scope){
11391         var data = this.snapshot || this.data;
11392         return data.filterBy(fn, scope||this);
11393     },
11394
11395     /**
11396      * Collects unique values for a particular dataIndex from this store.
11397      * @param {String} dataIndex The property to collect
11398      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11399      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11400      * @return {Array} An array of the unique values
11401      **/
11402     collect : function(dataIndex, allowNull, bypassFilter){
11403         var d = (bypassFilter === true && this.snapshot) ?
11404                 this.snapshot.items : this.data.items;
11405         var v, sv, r = [], l = {};
11406         for(var i = 0, len = d.length; i < len; i++){
11407             v = d[i].data[dataIndex];
11408             sv = String(v);
11409             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11410                 l[sv] = true;
11411                 r[r.length] = v;
11412             }
11413         }
11414         return r;
11415     },
11416
11417     /**
11418      * Revert to a view of the Record cache with no filtering applied.
11419      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11420      */
11421     clearFilter : function(suppressEvent){
11422         if(this.snapshot && this.snapshot != this.data){
11423             this.data = this.snapshot;
11424             delete this.snapshot;
11425             if(suppressEvent !== true){
11426                 this.fireEvent("datachanged", this);
11427             }
11428         }
11429     },
11430
11431     // private
11432     afterEdit : function(record){
11433         if(this.modified.indexOf(record) == -1){
11434             this.modified.push(record);
11435         }
11436         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11437     },
11438     
11439     // private
11440     afterReject : function(record){
11441         this.modified.remove(record);
11442         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11443     },
11444
11445     // private
11446     afterCommit : function(record){
11447         this.modified.remove(record);
11448         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11449     },
11450
11451     /**
11452      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11453      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11454      */
11455     commitChanges : function(){
11456         var m = this.modified.slice(0);
11457         this.modified = [];
11458         for(var i = 0, len = m.length; i < len; i++){
11459             m[i].commit();
11460         }
11461     },
11462
11463     /**
11464      * Cancel outstanding changes on all changed records.
11465      */
11466     rejectChanges : function(){
11467         var m = this.modified.slice(0);
11468         this.modified = [];
11469         for(var i = 0, len = m.length; i < len; i++){
11470             m[i].reject();
11471         }
11472     },
11473
11474     onMetaChange : function(meta, rtype, o){
11475         this.recordType = rtype;
11476         this.fields = rtype.prototype.fields;
11477         delete this.snapshot;
11478         this.sortInfo = meta.sortInfo || this.sortInfo;
11479         this.modified = [];
11480         this.fireEvent('metachange', this, this.reader.meta);
11481     },
11482     
11483     moveIndex : function(data, type)
11484     {
11485         var index = this.indexOf(data);
11486         
11487         var newIndex = index + type;
11488         
11489         this.remove(data);
11490         
11491         this.insert(newIndex, data);
11492         
11493     }
11494 });/*
11495  * Based on:
11496  * Ext JS Library 1.1.1
11497  * Copyright(c) 2006-2007, Ext JS, LLC.
11498  *
11499  * Originally Released Under LGPL - original licence link has changed is not relivant.
11500  *
11501  * Fork - LGPL
11502  * <script type="text/javascript">
11503  */
11504
11505 /**
11506  * @class Roo.data.SimpleStore
11507  * @extends Roo.data.Store
11508  * Small helper class to make creating Stores from Array data easier.
11509  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11510  * @cfg {Array} fields An array of field definition objects, or field name strings.
11511  * @cfg {Array} data The multi-dimensional array of data
11512  * @constructor
11513  * @param {Object} config
11514  */
11515 Roo.data.SimpleStore = function(config){
11516     Roo.data.SimpleStore.superclass.constructor.call(this, {
11517         isLocal : true,
11518         reader: new Roo.data.ArrayReader({
11519                 id: config.id
11520             },
11521             Roo.data.Record.create(config.fields)
11522         ),
11523         proxy : new Roo.data.MemoryProxy(config.data)
11524     });
11525     this.load();
11526 };
11527 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11528  * Based on:
11529  * Ext JS Library 1.1.1
11530  * Copyright(c) 2006-2007, Ext JS, LLC.
11531  *
11532  * Originally Released Under LGPL - original licence link has changed is not relivant.
11533  *
11534  * Fork - LGPL
11535  * <script type="text/javascript">
11536  */
11537
11538 /**
11539 /**
11540  * @extends Roo.data.Store
11541  * @class Roo.data.JsonStore
11542  * Small helper class to make creating Stores for JSON data easier. <br/>
11543 <pre><code>
11544 var store = new Roo.data.JsonStore({
11545     url: 'get-images.php',
11546     root: 'images',
11547     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11548 });
11549 </code></pre>
11550  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11551  * JsonReader and HttpProxy (unless inline data is provided).</b>
11552  * @cfg {Array} fields An array of field definition objects, or field name strings.
11553  * @constructor
11554  * @param {Object} config
11555  */
11556 Roo.data.JsonStore = function(c){
11557     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11558         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11559         reader: new Roo.data.JsonReader(c, c.fields)
11560     }));
11561 };
11562 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11563  * Based on:
11564  * Ext JS Library 1.1.1
11565  * Copyright(c) 2006-2007, Ext JS, LLC.
11566  *
11567  * Originally Released Under LGPL - original licence link has changed is not relivant.
11568  *
11569  * Fork - LGPL
11570  * <script type="text/javascript">
11571  */
11572
11573  
11574 Roo.data.Field = function(config){
11575     if(typeof config == "string"){
11576         config = {name: config};
11577     }
11578     Roo.apply(this, config);
11579     
11580     if(!this.type){
11581         this.type = "auto";
11582     }
11583     
11584     var st = Roo.data.SortTypes;
11585     // named sortTypes are supported, here we look them up
11586     if(typeof this.sortType == "string"){
11587         this.sortType = st[this.sortType];
11588     }
11589     
11590     // set default sortType for strings and dates
11591     if(!this.sortType){
11592         switch(this.type){
11593             case "string":
11594                 this.sortType = st.asUCString;
11595                 break;
11596             case "date":
11597                 this.sortType = st.asDate;
11598                 break;
11599             default:
11600                 this.sortType = st.none;
11601         }
11602     }
11603
11604     // define once
11605     var stripRe = /[\$,%]/g;
11606
11607     // prebuilt conversion function for this field, instead of
11608     // switching every time we're reading a value
11609     if(!this.convert){
11610         var cv, dateFormat = this.dateFormat;
11611         switch(this.type){
11612             case "":
11613             case "auto":
11614             case undefined:
11615                 cv = function(v){ return v; };
11616                 break;
11617             case "string":
11618                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11619                 break;
11620             case "int":
11621                 cv = function(v){
11622                     return v !== undefined && v !== null && v !== '' ?
11623                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11624                     };
11625                 break;
11626             case "float":
11627                 cv = function(v){
11628                     return v !== undefined && v !== null && v !== '' ?
11629                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11630                     };
11631                 break;
11632             case "bool":
11633             case "boolean":
11634                 cv = function(v){ return v === true || v === "true" || v == 1; };
11635                 break;
11636             case "date":
11637                 cv = function(v){
11638                     if(!v){
11639                         return '';
11640                     }
11641                     if(v instanceof Date){
11642                         return v;
11643                     }
11644                     if(dateFormat){
11645                         if(dateFormat == "timestamp"){
11646                             return new Date(v*1000);
11647                         }
11648                         return Date.parseDate(v, dateFormat);
11649                     }
11650                     var parsed = Date.parse(v);
11651                     return parsed ? new Date(parsed) : null;
11652                 };
11653              break;
11654             
11655         }
11656         this.convert = cv;
11657     }
11658 };
11659
11660 Roo.data.Field.prototype = {
11661     dateFormat: null,
11662     defaultValue: "",
11663     mapping: null,
11664     sortType : null,
11665     sortDir : "ASC"
11666 };/*
11667  * Based on:
11668  * Ext JS Library 1.1.1
11669  * Copyright(c) 2006-2007, Ext JS, LLC.
11670  *
11671  * Originally Released Under LGPL - original licence link has changed is not relivant.
11672  *
11673  * Fork - LGPL
11674  * <script type="text/javascript">
11675  */
11676  
11677 // Base class for reading structured data from a data source.  This class is intended to be
11678 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11679
11680 /**
11681  * @class Roo.data.DataReader
11682  * Base class for reading structured data from a data source.  This class is intended to be
11683  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11684  */
11685
11686 Roo.data.DataReader = function(meta, recordType){
11687     
11688     this.meta = meta;
11689     
11690     this.recordType = recordType instanceof Array ? 
11691         Roo.data.Record.create(recordType) : recordType;
11692 };
11693
11694 Roo.data.DataReader.prototype = {
11695      /**
11696      * Create an empty record
11697      * @param {Object} data (optional) - overlay some values
11698      * @return {Roo.data.Record} record created.
11699      */
11700     newRow :  function(d) {
11701         var da =  {};
11702         this.recordType.prototype.fields.each(function(c) {
11703             switch( c.type) {
11704                 case 'int' : da[c.name] = 0; break;
11705                 case 'date' : da[c.name] = new Date(); break;
11706                 case 'float' : da[c.name] = 0.0; break;
11707                 case 'boolean' : da[c.name] = false; break;
11708                 default : da[c.name] = ""; break;
11709             }
11710             
11711         });
11712         return new this.recordType(Roo.apply(da, d));
11713     }
11714     
11715 };/*
11716  * Based on:
11717  * Ext JS Library 1.1.1
11718  * Copyright(c) 2006-2007, Ext JS, LLC.
11719  *
11720  * Originally Released Under LGPL - original licence link has changed is not relivant.
11721  *
11722  * Fork - LGPL
11723  * <script type="text/javascript">
11724  */
11725
11726 /**
11727  * @class Roo.data.DataProxy
11728  * @extends Roo.data.Observable
11729  * This class is an abstract base class for implementations which provide retrieval of
11730  * unformatted data objects.<br>
11731  * <p>
11732  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11733  * (of the appropriate type which knows how to parse the data object) to provide a block of
11734  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11735  * <p>
11736  * Custom implementations must implement the load method as described in
11737  * {@link Roo.data.HttpProxy#load}.
11738  */
11739 Roo.data.DataProxy = function(){
11740     this.addEvents({
11741         /**
11742          * @event beforeload
11743          * Fires before a network request is made to retrieve a data object.
11744          * @param {Object} This DataProxy object.
11745          * @param {Object} params The params parameter to the load function.
11746          */
11747         beforeload : true,
11748         /**
11749          * @event load
11750          * Fires before the load method's callback is called.
11751          * @param {Object} This DataProxy object.
11752          * @param {Object} o The data object.
11753          * @param {Object} arg The callback argument object passed to the load function.
11754          */
11755         load : true,
11756         /**
11757          * @event loadexception
11758          * Fires if an Exception occurs during data retrieval.
11759          * @param {Object} This DataProxy object.
11760          * @param {Object} o The data object.
11761          * @param {Object} arg The callback argument object passed to the load function.
11762          * @param {Object} e The Exception.
11763          */
11764         loadexception : true
11765     });
11766     Roo.data.DataProxy.superclass.constructor.call(this);
11767 };
11768
11769 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11770
11771     /**
11772      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11773      */
11774 /*
11775  * Based on:
11776  * Ext JS Library 1.1.1
11777  * Copyright(c) 2006-2007, Ext JS, LLC.
11778  *
11779  * Originally Released Under LGPL - original licence link has changed is not relivant.
11780  *
11781  * Fork - LGPL
11782  * <script type="text/javascript">
11783  */
11784 /**
11785  * @class Roo.data.MemoryProxy
11786  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11787  * to the Reader when its load method is called.
11788  * @constructor
11789  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11790  */
11791 Roo.data.MemoryProxy = function(data){
11792     if (data.data) {
11793         data = data.data;
11794     }
11795     Roo.data.MemoryProxy.superclass.constructor.call(this);
11796     this.data = data;
11797 };
11798
11799 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11800     
11801     /**
11802      * Load data from the requested source (in this case an in-memory
11803      * data object passed to the constructor), read the data object into
11804      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11805      * process that block using the passed callback.
11806      * @param {Object} params This parameter is not used by the MemoryProxy class.
11807      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11808      * object into a block of Roo.data.Records.
11809      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11810      * The function must be passed <ul>
11811      * <li>The Record block object</li>
11812      * <li>The "arg" argument from the load function</li>
11813      * <li>A boolean success indicator</li>
11814      * </ul>
11815      * @param {Object} scope The scope in which to call the callback
11816      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11817      */
11818     load : function(params, reader, callback, scope, arg){
11819         params = params || {};
11820         var result;
11821         try {
11822             result = reader.readRecords(this.data);
11823         }catch(e){
11824             this.fireEvent("loadexception", this, arg, null, e);
11825             callback.call(scope, null, arg, false);
11826             return;
11827         }
11828         callback.call(scope, result, arg, true);
11829     },
11830     
11831     // private
11832     update : function(params, records){
11833         
11834     }
11835 });/*
11836  * Based on:
11837  * Ext JS Library 1.1.1
11838  * Copyright(c) 2006-2007, Ext JS, LLC.
11839  *
11840  * Originally Released Under LGPL - original licence link has changed is not relivant.
11841  *
11842  * Fork - LGPL
11843  * <script type="text/javascript">
11844  */
11845 /**
11846  * @class Roo.data.HttpProxy
11847  * @extends Roo.data.DataProxy
11848  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11849  * configured to reference a certain URL.<br><br>
11850  * <p>
11851  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11852  * from which the running page was served.<br><br>
11853  * <p>
11854  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11855  * <p>
11856  * Be aware that to enable the browser to parse an XML document, the server must set
11857  * the Content-Type header in the HTTP response to "text/xml".
11858  * @constructor
11859  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11860  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11861  * will be used to make the request.
11862  */
11863 Roo.data.HttpProxy = function(conn){
11864     Roo.data.HttpProxy.superclass.constructor.call(this);
11865     // is conn a conn config or a real conn?
11866     this.conn = conn;
11867     this.useAjax = !conn || !conn.events;
11868   
11869 };
11870
11871 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11872     // thse are take from connection...
11873     
11874     /**
11875      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11876      */
11877     /**
11878      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11879      * extra parameters to each request made by this object. (defaults to undefined)
11880      */
11881     /**
11882      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11883      *  to each request made by this object. (defaults to undefined)
11884      */
11885     /**
11886      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11887      */
11888     /**
11889      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11890      */
11891      /**
11892      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11893      * @type Boolean
11894      */
11895   
11896
11897     /**
11898      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11899      * @type Boolean
11900      */
11901     /**
11902      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11903      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11904      * a finer-grained basis than the DataProxy events.
11905      */
11906     getConnection : function(){
11907         return this.useAjax ? Roo.Ajax : this.conn;
11908     },
11909
11910     /**
11911      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11912      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11913      * process that block using the passed callback.
11914      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11915      * for the request to the remote server.
11916      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11917      * object into a block of Roo.data.Records.
11918      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11919      * The function must be passed <ul>
11920      * <li>The Record block object</li>
11921      * <li>The "arg" argument from the load function</li>
11922      * <li>A boolean success indicator</li>
11923      * </ul>
11924      * @param {Object} scope The scope in which to call the callback
11925      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11926      */
11927     load : function(params, reader, callback, scope, arg){
11928         if(this.fireEvent("beforeload", this, params) !== false){
11929             var  o = {
11930                 params : params || {},
11931                 request: {
11932                     callback : callback,
11933                     scope : scope,
11934                     arg : arg
11935                 },
11936                 reader: reader,
11937                 callback : this.loadResponse,
11938                 scope: this
11939             };
11940             if(this.useAjax){
11941                 Roo.applyIf(o, this.conn);
11942                 if(this.activeRequest){
11943                     Roo.Ajax.abort(this.activeRequest);
11944                 }
11945                 this.activeRequest = Roo.Ajax.request(o);
11946             }else{
11947                 this.conn.request(o);
11948             }
11949         }else{
11950             callback.call(scope||this, null, arg, false);
11951         }
11952     },
11953
11954     // private
11955     loadResponse : function(o, success, response){
11956         delete this.activeRequest;
11957         if(!success){
11958             this.fireEvent("loadexception", this, o, response);
11959             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11960             return;
11961         }
11962         var result;
11963         try {
11964             result = o.reader.read(response);
11965         }catch(e){
11966             this.fireEvent("loadexception", this, o, response, e);
11967             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11968             return;
11969         }
11970         
11971         this.fireEvent("load", this, o, o.request.arg);
11972         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11973     },
11974
11975     // private
11976     update : function(dataSet){
11977
11978     },
11979
11980     // private
11981     updateResponse : function(dataSet){
11982
11983     }
11984 });/*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994
11995 /**
11996  * @class Roo.data.ScriptTagProxy
11997  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11998  * other than the originating domain of the running page.<br><br>
11999  * <p>
12000  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12001  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12002  * <p>
12003  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12004  * source code that is used as the source inside a &lt;script> tag.<br><br>
12005  * <p>
12006  * In order for the browser to process the returned data, the server must wrap the data object
12007  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12008  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12009  * depending on whether the callback name was passed:
12010  * <p>
12011  * <pre><code>
12012 boolean scriptTag = false;
12013 String cb = request.getParameter("callback");
12014 if (cb != null) {
12015     scriptTag = true;
12016     response.setContentType("text/javascript");
12017 } else {
12018     response.setContentType("application/x-json");
12019 }
12020 Writer out = response.getWriter();
12021 if (scriptTag) {
12022     out.write(cb + "(");
12023 }
12024 out.print(dataBlock.toJsonString());
12025 if (scriptTag) {
12026     out.write(");");
12027 }
12028 </pre></code>
12029  *
12030  * @constructor
12031  * @param {Object} config A configuration object.
12032  */
12033 Roo.data.ScriptTagProxy = function(config){
12034     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12035     Roo.apply(this, config);
12036     this.head = document.getElementsByTagName("head")[0];
12037 };
12038
12039 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12040
12041 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12042     /**
12043      * @cfg {String} url The URL from which to request the data object.
12044      */
12045     /**
12046      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12047      */
12048     timeout : 30000,
12049     /**
12050      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12051      * the server the name of the callback function set up by the load call to process the returned data object.
12052      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12053      * javascript output which calls this named function passing the data object as its only parameter.
12054      */
12055     callbackParam : "callback",
12056     /**
12057      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12058      * name to the request.
12059      */
12060     nocache : true,
12061
12062     /**
12063      * Load data from the configured URL, read the data object into
12064      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12065      * process that block using the passed callback.
12066      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12067      * for the request to the remote server.
12068      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12069      * object into a block of Roo.data.Records.
12070      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12071      * The function must be passed <ul>
12072      * <li>The Record block object</li>
12073      * <li>The "arg" argument from the load function</li>
12074      * <li>A boolean success indicator</li>
12075      * </ul>
12076      * @param {Object} scope The scope in which to call the callback
12077      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12078      */
12079     load : function(params, reader, callback, scope, arg){
12080         if(this.fireEvent("beforeload", this, params) !== false){
12081
12082             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12083
12084             var url = this.url;
12085             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12086             if(this.nocache){
12087                 url += "&_dc=" + (new Date().getTime());
12088             }
12089             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12090             var trans = {
12091                 id : transId,
12092                 cb : "stcCallback"+transId,
12093                 scriptId : "stcScript"+transId,
12094                 params : params,
12095                 arg : arg,
12096                 url : url,
12097                 callback : callback,
12098                 scope : scope,
12099                 reader : reader
12100             };
12101             var conn = this;
12102
12103             window[trans.cb] = function(o){
12104                 conn.handleResponse(o, trans);
12105             };
12106
12107             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12108
12109             if(this.autoAbort !== false){
12110                 this.abort();
12111             }
12112
12113             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12114
12115             var script = document.createElement("script");
12116             script.setAttribute("src", url);
12117             script.setAttribute("type", "text/javascript");
12118             script.setAttribute("id", trans.scriptId);
12119             this.head.appendChild(script);
12120
12121             this.trans = trans;
12122         }else{
12123             callback.call(scope||this, null, arg, false);
12124         }
12125     },
12126
12127     // private
12128     isLoading : function(){
12129         return this.trans ? true : false;
12130     },
12131
12132     /**
12133      * Abort the current server request.
12134      */
12135     abort : function(){
12136         if(this.isLoading()){
12137             this.destroyTrans(this.trans);
12138         }
12139     },
12140
12141     // private
12142     destroyTrans : function(trans, isLoaded){
12143         this.head.removeChild(document.getElementById(trans.scriptId));
12144         clearTimeout(trans.timeoutId);
12145         if(isLoaded){
12146             window[trans.cb] = undefined;
12147             try{
12148                 delete window[trans.cb];
12149             }catch(e){}
12150         }else{
12151             // if hasn't been loaded, wait for load to remove it to prevent script error
12152             window[trans.cb] = function(){
12153                 window[trans.cb] = undefined;
12154                 try{
12155                     delete window[trans.cb];
12156                 }catch(e){}
12157             };
12158         }
12159     },
12160
12161     // private
12162     handleResponse : function(o, trans){
12163         this.trans = false;
12164         this.destroyTrans(trans, true);
12165         var result;
12166         try {
12167             result = trans.reader.readRecords(o);
12168         }catch(e){
12169             this.fireEvent("loadexception", this, o, trans.arg, e);
12170             trans.callback.call(trans.scope||window, null, trans.arg, false);
12171             return;
12172         }
12173         this.fireEvent("load", this, o, trans.arg);
12174         trans.callback.call(trans.scope||window, result, trans.arg, true);
12175     },
12176
12177     // private
12178     handleFailure : function(trans){
12179         this.trans = false;
12180         this.destroyTrans(trans, false);
12181         this.fireEvent("loadexception", this, null, trans.arg);
12182         trans.callback.call(trans.scope||window, null, trans.arg, false);
12183     }
12184 });/*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194
12195 /**
12196  * @class Roo.data.JsonReader
12197  * @extends Roo.data.DataReader
12198  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12199  * based on mappings in a provided Roo.data.Record constructor.
12200  * 
12201  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12202  * in the reply previously. 
12203  * 
12204  * <p>
12205  * Example code:
12206  * <pre><code>
12207 var RecordDef = Roo.data.Record.create([
12208     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12209     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12210 ]);
12211 var myReader = new Roo.data.JsonReader({
12212     totalProperty: "results",    // The property which contains the total dataset size (optional)
12213     root: "rows",                // The property which contains an Array of row objects
12214     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12215 }, RecordDef);
12216 </code></pre>
12217  * <p>
12218  * This would consume a JSON file like this:
12219  * <pre><code>
12220 { 'results': 2, 'rows': [
12221     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12222     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12223 }
12224 </code></pre>
12225  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12226  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12227  * paged from the remote server.
12228  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12229  * @cfg {String} root name of the property which contains the Array of row objects.
12230  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12231  * @cfg {Array} fields Array of field definition objects
12232  * @constructor
12233  * Create a new JsonReader
12234  * @param {Object} meta Metadata configuration options
12235  * @param {Object} recordType Either an Array of field definition objects,
12236  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12237  */
12238 Roo.data.JsonReader = function(meta, recordType){
12239     
12240     meta = meta || {};
12241     // set some defaults:
12242     Roo.applyIf(meta, {
12243         totalProperty: 'total',
12244         successProperty : 'success',
12245         root : 'data',
12246         id : 'id'
12247     });
12248     
12249     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12250 };
12251 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12252     
12253     /**
12254      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12255      * Used by Store query builder to append _requestMeta to params.
12256      * 
12257      */
12258     metaFromRemote : false,
12259     /**
12260      * This method is only used by a DataProxy which has retrieved data from a remote server.
12261      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     read : function(response){
12266         var json = response.responseText;
12267        
12268         var o = /* eval:var:o */ eval("("+json+")");
12269         if(!o) {
12270             throw {message: "JsonReader.read: Json object not found"};
12271         }
12272         
12273         if(o.metaData){
12274             
12275             delete this.ef;
12276             this.metaFromRemote = true;
12277             this.meta = o.metaData;
12278             this.recordType = Roo.data.Record.create(o.metaData.fields);
12279             this.onMetaChange(this.meta, this.recordType, o);
12280         }
12281         return this.readRecords(o);
12282     },
12283
12284     // private function a store will implement
12285     onMetaChange : function(meta, recordType, o){
12286
12287     },
12288
12289     /**
12290          * @ignore
12291          */
12292     simpleAccess: function(obj, subsc) {
12293         return obj[subsc];
12294     },
12295
12296         /**
12297          * @ignore
12298          */
12299     getJsonAccessor: function(){
12300         var re = /[\[\.]/;
12301         return function(expr) {
12302             try {
12303                 return(re.test(expr))
12304                     ? new Function("obj", "return obj." + expr)
12305                     : function(obj){
12306                         return obj[expr];
12307                     };
12308             } catch(e){}
12309             return Roo.emptyFn;
12310         };
12311     }(),
12312
12313     /**
12314      * Create a data block containing Roo.data.Records from an XML document.
12315      * @param {Object} o An object which contains an Array of row objects in the property specified
12316      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12317      * which contains the total size of the dataset.
12318      * @return {Object} data A data block which is used by an Roo.data.Store object as
12319      * a cache of Roo.data.Records.
12320      */
12321     readRecords : function(o){
12322         /**
12323          * After any data loads, the raw JSON data is available for further custom processing.
12324          * @type Object
12325          */
12326         this.o = o;
12327         var s = this.meta, Record = this.recordType,
12328             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12329
12330 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12331         if (!this.ef) {
12332             if(s.totalProperty) {
12333                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12334                 }
12335                 if(s.successProperty) {
12336                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12337                 }
12338                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12339                 if (s.id) {
12340                         var g = this.getJsonAccessor(s.id);
12341                         this.getId = function(rec) {
12342                                 var r = g(rec);  
12343                                 return (r === undefined || r === "") ? null : r;
12344                         };
12345                 } else {
12346                         this.getId = function(){return null;};
12347                 }
12348             this.ef = [];
12349             for(var jj = 0; jj < fl; jj++){
12350                 f = fi[jj];
12351                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12352                 this.ef[jj] = this.getJsonAccessor(map);
12353             }
12354         }
12355
12356         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12357         if(s.totalProperty){
12358             var vt = parseInt(this.getTotal(o), 10);
12359             if(!isNaN(vt)){
12360                 totalRecords = vt;
12361             }
12362         }
12363         if(s.successProperty){
12364             var vs = this.getSuccess(o);
12365             if(vs === false || vs === 'false'){
12366                 success = false;
12367             }
12368         }
12369         var records = [];
12370         for(var i = 0; i < c; i++){
12371                 var n = root[i];
12372             var values = {};
12373             var id = this.getId(n);
12374             for(var j = 0; j < fl; j++){
12375                 f = fi[j];
12376             var v = this.ef[j](n);
12377             if (!f.convert) {
12378                 Roo.log('missing convert for ' + f.name);
12379                 Roo.log(f);
12380                 continue;
12381             }
12382             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12383             }
12384             var record = new Record(values, id);
12385             record.json = n;
12386             records[i] = record;
12387         }
12388         return {
12389             raw : o,
12390             success : success,
12391             records : records,
12392             totalRecords : totalRecords
12393         };
12394     }
12395 });/*
12396  * Based on:
12397  * Ext JS Library 1.1.1
12398  * Copyright(c) 2006-2007, Ext JS, LLC.
12399  *
12400  * Originally Released Under LGPL - original licence link has changed is not relivant.
12401  *
12402  * Fork - LGPL
12403  * <script type="text/javascript">
12404  */
12405
12406 /**
12407  * @class Roo.data.ArrayReader
12408  * @extends Roo.data.DataReader
12409  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12410  * Each element of that Array represents a row of data fields. The
12411  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12412  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12413  * <p>
12414  * Example code:.
12415  * <pre><code>
12416 var RecordDef = Roo.data.Record.create([
12417     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12418     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12419 ]);
12420 var myReader = new Roo.data.ArrayReader({
12421     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12422 }, RecordDef);
12423 </code></pre>
12424  * <p>
12425  * This would consume an Array like this:
12426  * <pre><code>
12427 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12428   </code></pre>
12429  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12430  * @constructor
12431  * Create a new JsonReader
12432  * @param {Object} meta Metadata configuration options.
12433  * @param {Object} recordType Either an Array of field definition objects
12434  * as specified to {@link Roo.data.Record#create},
12435  * or an {@link Roo.data.Record} object
12436  * created using {@link Roo.data.Record#create}.
12437  */
12438 Roo.data.ArrayReader = function(meta, recordType){
12439     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12440 };
12441
12442 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12443     /**
12444      * Create a data block containing Roo.data.Records from an XML document.
12445      * @param {Object} o An Array of row objects which represents the dataset.
12446      * @return {Object} data A data block which is used by an Roo.data.Store object as
12447      * a cache of Roo.data.Records.
12448      */
12449     readRecords : function(o){
12450         var sid = this.meta ? this.meta.id : null;
12451         var recordType = this.recordType, fields = recordType.prototype.fields;
12452         var records = [];
12453         var root = o;
12454             for(var i = 0; i < root.length; i++){
12455                     var n = root[i];
12456                 var values = {};
12457                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12458                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12459                 var f = fields.items[j];
12460                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12461                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12462                 v = f.convert(v);
12463                 values[f.name] = v;
12464             }
12465                 var record = new recordType(values, id);
12466                 record.json = n;
12467                 records[records.length] = record;
12468             }
12469             return {
12470                 records : records,
12471                 totalRecords : records.length
12472             };
12473     }
12474 });/*
12475  * - LGPL
12476  * * 
12477  */
12478
12479 /**
12480  * @class Roo.bootstrap.ComboBox
12481  * @extends Roo.bootstrap.TriggerField
12482  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12483  * @cfg {Boolean} append (true|false) default false
12484  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12485  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12486  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12487  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12488  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12489  * @cfg {Boolean} animate default true
12490  * @cfg {Boolean} emptyResultText only for touch device
12491  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12492  * @cfg {String} emptyTitle default ''
12493  * @constructor
12494  * Create a new ComboBox.
12495  * @param {Object} config Configuration options
12496  */
12497 Roo.bootstrap.ComboBox = function(config){
12498     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12499     this.addEvents({
12500         /**
12501          * @event expand
12502          * Fires when the dropdown list is expanded
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         */
12505         'expand' : true,
12506         /**
12507          * @event collapse
12508          * Fires when the dropdown list is collapsed
12509         * @param {Roo.bootstrap.ComboBox} combo This combo box
12510         */
12511         'collapse' : true,
12512         /**
12513          * @event beforeselect
12514          * Fires before a list item is selected. Return false to cancel the selection.
12515         * @param {Roo.bootstrap.ComboBox} combo This combo box
12516         * @param {Roo.data.Record} record The data record returned from the underlying store
12517         * @param {Number} index The index of the selected item in the dropdown list
12518         */
12519         'beforeselect' : true,
12520         /**
12521          * @event select
12522          * Fires when a list item is selected
12523         * @param {Roo.bootstrap.ComboBox} combo This combo box
12524         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12525         * @param {Number} index The index of the selected item in the dropdown list
12526         */
12527         'select' : true,
12528         /**
12529          * @event beforequery
12530          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12531          * The event object passed has these properties:
12532         * @param {Roo.bootstrap.ComboBox} combo This combo box
12533         * @param {String} query The query
12534         * @param {Boolean} forceAll true to force "all" query
12535         * @param {Boolean} cancel true to cancel the query
12536         * @param {Object} e The query event object
12537         */
12538         'beforequery': true,
12539          /**
12540          * @event add
12541          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12542         * @param {Roo.bootstrap.ComboBox} combo This combo box
12543         */
12544         'add' : true,
12545         /**
12546          * @event edit
12547          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12548         * @param {Roo.bootstrap.ComboBox} combo This combo box
12549         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12550         */
12551         'edit' : true,
12552         /**
12553          * @event remove
12554          * Fires when the remove value from the combobox array
12555         * @param {Roo.bootstrap.ComboBox} combo This combo box
12556         */
12557         'remove' : true,
12558         /**
12559          * @event afterremove
12560          * Fires when the remove value from the combobox array
12561         * @param {Roo.bootstrap.ComboBox} combo This combo box
12562         */
12563         'afterremove' : true,
12564         /**
12565          * @event specialfilter
12566          * Fires when specialfilter
12567             * @param {Roo.bootstrap.ComboBox} combo This combo box
12568             */
12569         'specialfilter' : true,
12570         /**
12571          * @event tick
12572          * Fires when tick the element
12573             * @param {Roo.bootstrap.ComboBox} combo This combo box
12574             */
12575         'tick' : true,
12576         /**
12577          * @event touchviewdisplay
12578          * Fires when touch view require special display (default is using displayField)
12579             * @param {Roo.bootstrap.ComboBox} combo This combo box
12580             * @param {Object} cfg set html .
12581             */
12582         'touchviewdisplay' : true
12583         
12584     });
12585     
12586     this.item = [];
12587     this.tickItems = [];
12588     
12589     this.selectedIndex = -1;
12590     if(this.mode == 'local'){
12591         if(config.queryDelay === undefined){
12592             this.queryDelay = 10;
12593         }
12594         if(config.minChars === undefined){
12595             this.minChars = 0;
12596         }
12597     }
12598 };
12599
12600 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12601      
12602     /**
12603      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12604      * rendering into an Roo.Editor, defaults to false)
12605      */
12606     /**
12607      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12608      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12609      */
12610     /**
12611      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12612      */
12613     /**
12614      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12615      * the dropdown list (defaults to undefined, with no header element)
12616      */
12617
12618      /**
12619      * @cfg {String/Roo.Template} tpl The template to use to render the output
12620      */
12621      
12622      /**
12623      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12624      */
12625     listWidth: undefined,
12626     /**
12627      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12628      * mode = 'remote' or 'text' if mode = 'local')
12629      */
12630     displayField: undefined,
12631     
12632     /**
12633      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12634      * mode = 'remote' or 'value' if mode = 'local'). 
12635      * Note: use of a valueField requires the user make a selection
12636      * in order for a value to be mapped.
12637      */
12638     valueField: undefined,
12639     /**
12640      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12641      */
12642     modalTitle : '',
12643     
12644     /**
12645      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12646      * field's data value (defaults to the underlying DOM element's name)
12647      */
12648     hiddenName: undefined,
12649     /**
12650      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12651      */
12652     listClass: '',
12653     /**
12654      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12655      */
12656     selectedClass: 'active',
12657     
12658     /**
12659      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12660      */
12661     shadow:'sides',
12662     /**
12663      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12664      * anchor positions (defaults to 'tl-bl')
12665      */
12666     listAlign: 'tl-bl?',
12667     /**
12668      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12669      */
12670     maxHeight: 300,
12671     /**
12672      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12673      * query specified by the allQuery config option (defaults to 'query')
12674      */
12675     triggerAction: 'query',
12676     /**
12677      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12678      * (defaults to 4, does not apply if editable = false)
12679      */
12680     minChars : 4,
12681     /**
12682      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12683      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12684      */
12685     typeAhead: false,
12686     /**
12687      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12688      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12689      */
12690     queryDelay: 500,
12691     /**
12692      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12693      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12694      */
12695     pageSize: 0,
12696     /**
12697      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12698      * when editable = true (defaults to false)
12699      */
12700     selectOnFocus:false,
12701     /**
12702      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12703      */
12704     queryParam: 'query',
12705     /**
12706      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12707      * when mode = 'remote' (defaults to 'Loading...')
12708      */
12709     loadingText: 'Loading...',
12710     /**
12711      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12712      */
12713     resizable: false,
12714     /**
12715      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12716      */
12717     handleHeight : 8,
12718     /**
12719      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12720      * traditional select (defaults to true)
12721      */
12722     editable: true,
12723     /**
12724      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12725      */
12726     allQuery: '',
12727     /**
12728      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12729      */
12730     mode: 'remote',
12731     /**
12732      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12733      * listWidth has a higher value)
12734      */
12735     minListWidth : 70,
12736     /**
12737      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12738      * allow the user to set arbitrary text into the field (defaults to false)
12739      */
12740     forceSelection:false,
12741     /**
12742      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12743      * if typeAhead = true (defaults to 250)
12744      */
12745     typeAheadDelay : 250,
12746     /**
12747      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12748      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12749      */
12750     valueNotFoundText : undefined,
12751     /**
12752      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12753      */
12754     blockFocus : false,
12755     
12756     /**
12757      * @cfg {Boolean} disableClear Disable showing of clear button.
12758      */
12759     disableClear : false,
12760     /**
12761      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12762      */
12763     alwaysQuery : false,
12764     
12765     /**
12766      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12767      */
12768     multiple : false,
12769     
12770     /**
12771      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12772      */
12773     invalidClass : "has-warning",
12774     
12775     /**
12776      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12777      */
12778     validClass : "has-success",
12779     
12780     /**
12781      * @cfg {Boolean} specialFilter (true|false) special filter default false
12782      */
12783     specialFilter : false,
12784     
12785     /**
12786      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12787      */
12788     mobileTouchView : true,
12789     
12790     /**
12791      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12792      */
12793     useNativeIOS : false,
12794     
12795     ios_options : false,
12796     
12797     //private
12798     addicon : false,
12799     editicon: false,
12800     
12801     page: 0,
12802     hasQuery: false,
12803     append: false,
12804     loadNext: false,
12805     autoFocus : true,
12806     tickable : false,
12807     btnPosition : 'right',
12808     triggerList : true,
12809     showToggleBtn : true,
12810     animate : true,
12811     emptyResultText: 'Empty',
12812     triggerText : 'Select',
12813     emptyTitle : '',
12814     
12815     // element that contains real text value.. (when hidden is used..)
12816     
12817     getAutoCreate : function()
12818     {   
12819         var cfg = false;
12820         //render
12821         /*
12822          * Render classic select for iso
12823          */
12824         
12825         if(Roo.isIOS && this.useNativeIOS){
12826             cfg = this.getAutoCreateNativeIOS();
12827             return cfg;
12828         }
12829         
12830         /*
12831          * Touch Devices
12832          */
12833         
12834         if(Roo.isTouch && this.mobileTouchView){
12835             cfg = this.getAutoCreateTouchView();
12836             return cfg;;
12837         }
12838         
12839         /*
12840          *  Normal ComboBox
12841          */
12842         if(!this.tickable){
12843             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12844             return cfg;
12845         }
12846         
12847         /*
12848          *  ComboBox with tickable selections
12849          */
12850              
12851         var align = this.labelAlign || this.parentLabelAlign();
12852         
12853         cfg = {
12854             cls : 'form-group roo-combobox-tickable' //input-group
12855         };
12856         
12857         var btn_text_select = '';
12858         var btn_text_done = '';
12859         var btn_text_cancel = '';
12860         
12861         if (this.btn_text_show) {
12862             btn_text_select = 'Select';
12863             btn_text_done = 'Done';
12864             btn_text_cancel = 'Cancel'; 
12865         }
12866         
12867         var buttons = {
12868             tag : 'div',
12869             cls : 'tickable-buttons',
12870             cn : [
12871                 {
12872                     tag : 'button',
12873                     type : 'button',
12874                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12875                     //html : this.triggerText
12876                     html: btn_text_select
12877                 },
12878                 {
12879                     tag : 'button',
12880                     type : 'button',
12881                     name : 'ok',
12882                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12883                     //html : 'Done'
12884                     html: btn_text_done
12885                 },
12886                 {
12887                     tag : 'button',
12888                     type : 'button',
12889                     name : 'cancel',
12890                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12891                     //html : 'Cancel'
12892                     html: btn_text_cancel
12893                 }
12894             ]
12895         };
12896         
12897         if(this.editable){
12898             buttons.cn.unshift({
12899                 tag: 'input',
12900                 cls: 'roo-select2-search-field-input'
12901             });
12902         }
12903         
12904         var _this = this;
12905         
12906         Roo.each(buttons.cn, function(c){
12907             if (_this.size) {
12908                 c.cls += ' btn-' + _this.size;
12909             }
12910
12911             if (_this.disabled) {
12912                 c.disabled = true;
12913             }
12914         });
12915         
12916         var box = {
12917             tag: 'div',
12918             cn: [
12919                 {
12920                     tag: 'input',
12921                     type : 'hidden',
12922                     cls: 'form-hidden-field'
12923                 },
12924                 {
12925                     tag: 'ul',
12926                     cls: 'roo-select2-choices',
12927                     cn:[
12928                         {
12929                             tag: 'li',
12930                             cls: 'roo-select2-search-field',
12931                             cn: [
12932                                 buttons
12933                             ]
12934                         }
12935                     ]
12936                 }
12937             ]
12938         };
12939         
12940         var combobox = {
12941             cls: 'roo-select2-container input-group roo-select2-container-multi',
12942             cn: [
12943                 box
12944 //                {
12945 //                    tag: 'ul',
12946 //                    cls: 'typeahead typeahead-long dropdown-menu',
12947 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12948 //                }
12949             ]
12950         };
12951         
12952         if(this.hasFeedback && !this.allowBlank){
12953             
12954             var feedback = {
12955                 tag: 'span',
12956                 cls: 'glyphicon form-control-feedback'
12957             };
12958
12959             combobox.cn.push(feedback);
12960         }
12961         
12962         
12963         if (align ==='left' && this.fieldLabel.length) {
12964             
12965             cfg.cls += ' roo-form-group-label-left';
12966             
12967             cfg.cn = [
12968                 {
12969                     tag : 'i',
12970                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12971                     tooltip : 'This field is required'
12972                 },
12973                 {
12974                     tag: 'label',
12975                     'for' :  id,
12976                     cls : 'control-label',
12977                     html : this.fieldLabel
12978
12979                 },
12980                 {
12981                     cls : "", 
12982                     cn: [
12983                         combobox
12984                     ]
12985                 }
12986
12987             ];
12988             
12989             var labelCfg = cfg.cn[1];
12990             var contentCfg = cfg.cn[2];
12991             
12992
12993             if(this.indicatorpos == 'right'){
12994                 
12995                 cfg.cn = [
12996                     {
12997                         tag: 'label',
12998                         'for' :  id,
12999                         cls : 'control-label',
13000                         cn : [
13001                             {
13002                                 tag : 'span',
13003                                 html : this.fieldLabel
13004                             },
13005                             {
13006                                 tag : 'i',
13007                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13008                                 tooltip : 'This field is required'
13009                             }
13010                         ]
13011                     },
13012                     {
13013                         cls : "",
13014                         cn: [
13015                             combobox
13016                         ]
13017                     }
13018
13019                 ];
13020                 
13021                 
13022                 
13023                 labelCfg = cfg.cn[0];
13024                 contentCfg = cfg.cn[1];
13025             
13026             }
13027             
13028             if(this.labelWidth > 12){
13029                 labelCfg.style = "width: " + this.labelWidth + 'px';
13030             }
13031             
13032             if(this.labelWidth < 13 && this.labelmd == 0){
13033                 this.labelmd = this.labelWidth;
13034             }
13035             
13036             if(this.labellg > 0){
13037                 labelCfg.cls += ' col-lg-' + this.labellg;
13038                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13039             }
13040             
13041             if(this.labelmd > 0){
13042                 labelCfg.cls += ' col-md-' + this.labelmd;
13043                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13044             }
13045             
13046             if(this.labelsm > 0){
13047                 labelCfg.cls += ' col-sm-' + this.labelsm;
13048                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13049             }
13050             
13051             if(this.labelxs > 0){
13052                 labelCfg.cls += ' col-xs-' + this.labelxs;
13053                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13054             }
13055                 
13056                 
13057         } else if ( this.fieldLabel.length) {
13058 //                Roo.log(" label");
13059                  cfg.cn = [
13060                     {
13061                         tag : 'i',
13062                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13063                         tooltip : 'This field is required'
13064                     },
13065                     {
13066                         tag: 'label',
13067                         //cls : 'input-group-addon',
13068                         html : this.fieldLabel
13069                     },
13070                     combobox
13071                 ];
13072                 
13073                 if(this.indicatorpos == 'right'){
13074                     cfg.cn = [
13075                         {
13076                             tag: 'label',
13077                             //cls : 'input-group-addon',
13078                             html : this.fieldLabel
13079                         },
13080                         {
13081                             tag : 'i',
13082                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13083                             tooltip : 'This field is required'
13084                         },
13085                         combobox
13086                     ];
13087                     
13088                 }
13089
13090         } else {
13091             
13092 //                Roo.log(" no label && no align");
13093                 cfg = combobox
13094                      
13095                 
13096         }
13097          
13098         var settings=this;
13099         ['xs','sm','md','lg'].map(function(size){
13100             if (settings[size]) {
13101                 cfg.cls += ' col-' + size + '-' + settings[size];
13102             }
13103         });
13104         
13105         return cfg;
13106         
13107     },
13108     
13109     _initEventsCalled : false,
13110     
13111     // private
13112     initEvents: function()
13113     {   
13114         if (this._initEventsCalled) { // as we call render... prevent looping...
13115             return;
13116         }
13117         this._initEventsCalled = true;
13118         
13119         if (!this.store) {
13120             throw "can not find store for combo";
13121         }
13122         
13123         this.indicator = this.indicatorEl();
13124         
13125         this.store = Roo.factory(this.store, Roo.data);
13126         this.store.parent = this;
13127         
13128         // if we are building from html. then this element is so complex, that we can not really
13129         // use the rendered HTML.
13130         // so we have to trash and replace the previous code.
13131         if (Roo.XComponent.build_from_html) {
13132             // remove this element....
13133             var e = this.el.dom, k=0;
13134             while (e ) { e = e.previousSibling;  ++k;}
13135
13136             this.el.remove();
13137             
13138             this.el=false;
13139             this.rendered = false;
13140             
13141             this.render(this.parent().getChildContainer(true), k);
13142         }
13143         
13144         if(Roo.isIOS && this.useNativeIOS){
13145             this.initIOSView();
13146             return;
13147         }
13148         
13149         /*
13150          * Touch Devices
13151          */
13152         
13153         if(Roo.isTouch && this.mobileTouchView){
13154             this.initTouchView();
13155             return;
13156         }
13157         
13158         if(this.tickable){
13159             this.initTickableEvents();
13160             return;
13161         }
13162         
13163         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13164         
13165         if(this.hiddenName){
13166             
13167             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13168             
13169             this.hiddenField.dom.value =
13170                 this.hiddenValue !== undefined ? this.hiddenValue :
13171                 this.value !== undefined ? this.value : '';
13172
13173             // prevent input submission
13174             this.el.dom.removeAttribute('name');
13175             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13176              
13177              
13178         }
13179         //if(Roo.isGecko){
13180         //    this.el.dom.setAttribute('autocomplete', 'off');
13181         //}
13182         
13183         var cls = 'x-combo-list';
13184         
13185         //this.list = new Roo.Layer({
13186         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13187         //});
13188         
13189         var _this = this;
13190         
13191         (function(){
13192             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13193             _this.list.setWidth(lw);
13194         }).defer(100);
13195         
13196         this.list.on('mouseover', this.onViewOver, this);
13197         this.list.on('mousemove', this.onViewMove, this);
13198         this.list.on('scroll', this.onViewScroll, this);
13199         
13200         /*
13201         this.list.swallowEvent('mousewheel');
13202         this.assetHeight = 0;
13203
13204         if(this.title){
13205             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13206             this.assetHeight += this.header.getHeight();
13207         }
13208
13209         this.innerList = this.list.createChild({cls:cls+'-inner'});
13210         this.innerList.on('mouseover', this.onViewOver, this);
13211         this.innerList.on('mousemove', this.onViewMove, this);
13212         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13213         
13214         if(this.allowBlank && !this.pageSize && !this.disableClear){
13215             this.footer = this.list.createChild({cls:cls+'-ft'});
13216             this.pageTb = new Roo.Toolbar(this.footer);
13217            
13218         }
13219         if(this.pageSize){
13220             this.footer = this.list.createChild({cls:cls+'-ft'});
13221             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13222                     {pageSize: this.pageSize});
13223             
13224         }
13225         
13226         if (this.pageTb && this.allowBlank && !this.disableClear) {
13227             var _this = this;
13228             this.pageTb.add(new Roo.Toolbar.Fill(), {
13229                 cls: 'x-btn-icon x-btn-clear',
13230                 text: '&#160;',
13231                 handler: function()
13232                 {
13233                     _this.collapse();
13234                     _this.clearValue();
13235                     _this.onSelect(false, -1);
13236                 }
13237             });
13238         }
13239         if (this.footer) {
13240             this.assetHeight += this.footer.getHeight();
13241         }
13242         */
13243             
13244         if(!this.tpl){
13245             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13246         }
13247
13248         this.view = new Roo.View(this.list, this.tpl, {
13249             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13250         });
13251         //this.view.wrapEl.setDisplayed(false);
13252         this.view.on('click', this.onViewClick, this);
13253         
13254         
13255         this.store.on('beforeload', this.onBeforeLoad, this);
13256         this.store.on('load', this.onLoad, this);
13257         this.store.on('loadexception', this.onLoadException, this);
13258         /*
13259         if(this.resizable){
13260             this.resizer = new Roo.Resizable(this.list,  {
13261                pinned:true, handles:'se'
13262             });
13263             this.resizer.on('resize', function(r, w, h){
13264                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13265                 this.listWidth = w;
13266                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13267                 this.restrictHeight();
13268             }, this);
13269             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13270         }
13271         */
13272         if(!this.editable){
13273             this.editable = true;
13274             this.setEditable(false);
13275         }
13276         
13277         /*
13278         
13279         if (typeof(this.events.add.listeners) != 'undefined') {
13280             
13281             this.addicon = this.wrap.createChild(
13282                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13283        
13284             this.addicon.on('click', function(e) {
13285                 this.fireEvent('add', this);
13286             }, this);
13287         }
13288         if (typeof(this.events.edit.listeners) != 'undefined') {
13289             
13290             this.editicon = this.wrap.createChild(
13291                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13292             if (this.addicon) {
13293                 this.editicon.setStyle('margin-left', '40px');
13294             }
13295             this.editicon.on('click', function(e) {
13296                 
13297                 // we fire even  if inothing is selected..
13298                 this.fireEvent('edit', this, this.lastData );
13299                 
13300             }, this);
13301         }
13302         */
13303         
13304         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13305             "up" : function(e){
13306                 this.inKeyMode = true;
13307                 this.selectPrev();
13308             },
13309
13310             "down" : function(e){
13311                 if(!this.isExpanded()){
13312                     this.onTriggerClick();
13313                 }else{
13314                     this.inKeyMode = true;
13315                     this.selectNext();
13316                 }
13317             },
13318
13319             "enter" : function(e){
13320 //                this.onViewClick();
13321                 //return true;
13322                 this.collapse();
13323                 
13324                 if(this.fireEvent("specialkey", this, e)){
13325                     this.onViewClick(false);
13326                 }
13327                 
13328                 return true;
13329             },
13330
13331             "esc" : function(e){
13332                 this.collapse();
13333             },
13334
13335             "tab" : function(e){
13336                 this.collapse();
13337                 
13338                 if(this.fireEvent("specialkey", this, e)){
13339                     this.onViewClick(false);
13340                 }
13341                 
13342                 return true;
13343             },
13344
13345             scope : this,
13346
13347             doRelay : function(foo, bar, hname){
13348                 if(hname == 'down' || this.scope.isExpanded()){
13349                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13350                 }
13351                 return true;
13352             },
13353
13354             forceKeyDown: true
13355         });
13356         
13357         
13358         this.queryDelay = Math.max(this.queryDelay || 10,
13359                 this.mode == 'local' ? 10 : 250);
13360         
13361         
13362         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13363         
13364         if(this.typeAhead){
13365             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13366         }
13367         if(this.editable !== false){
13368             this.inputEl().on("keyup", this.onKeyUp, this);
13369         }
13370         if(this.forceSelection){
13371             this.inputEl().on('blur', this.doForce, this);
13372         }
13373         
13374         if(this.multiple){
13375             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13376             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13377         }
13378     },
13379     
13380     initTickableEvents: function()
13381     {   
13382         this.createList();
13383         
13384         if(this.hiddenName){
13385             
13386             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13387             
13388             this.hiddenField.dom.value =
13389                 this.hiddenValue !== undefined ? this.hiddenValue :
13390                 this.value !== undefined ? this.value : '';
13391
13392             // prevent input submission
13393             this.el.dom.removeAttribute('name');
13394             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13395              
13396              
13397         }
13398         
13399 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13400         
13401         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13402         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13403         if(this.triggerList){
13404             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13405         }
13406          
13407         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13408         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13409         
13410         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13411         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13412         
13413         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13414         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13415         
13416         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13417         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13418         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13419         
13420         this.okBtn.hide();
13421         this.cancelBtn.hide();
13422         
13423         var _this = this;
13424         
13425         (function(){
13426             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13427             _this.list.setWidth(lw);
13428         }).defer(100);
13429         
13430         this.list.on('mouseover', this.onViewOver, this);
13431         this.list.on('mousemove', this.onViewMove, this);
13432         
13433         this.list.on('scroll', this.onViewScroll, this);
13434         
13435         if(!this.tpl){
13436             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13437         }
13438
13439         this.view = new Roo.View(this.list, this.tpl, {
13440             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13441         });
13442         
13443         //this.view.wrapEl.setDisplayed(false);
13444         this.view.on('click', this.onViewClick, this);
13445         
13446         
13447         
13448         this.store.on('beforeload', this.onBeforeLoad, this);
13449         this.store.on('load', this.onLoad, this);
13450         this.store.on('loadexception', this.onLoadException, this);
13451         
13452         if(this.editable){
13453             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13454                 "up" : function(e){
13455                     this.inKeyMode = true;
13456                     this.selectPrev();
13457                 },
13458
13459                 "down" : function(e){
13460                     this.inKeyMode = true;
13461                     this.selectNext();
13462                 },
13463
13464                 "enter" : function(e){
13465                     if(this.fireEvent("specialkey", this, e)){
13466                         this.onViewClick(false);
13467                     }
13468                     
13469                     return true;
13470                 },
13471
13472                 "esc" : function(e){
13473                     this.onTickableFooterButtonClick(e, false, false);
13474                 },
13475
13476                 "tab" : function(e){
13477                     this.fireEvent("specialkey", this, e);
13478                     
13479                     this.onTickableFooterButtonClick(e, false, false);
13480                     
13481                     return true;
13482                 },
13483
13484                 scope : this,
13485
13486                 doRelay : function(e, fn, key){
13487                     if(this.scope.isExpanded()){
13488                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13489                     }
13490                     return true;
13491                 },
13492
13493                 forceKeyDown: true
13494             });
13495         }
13496         
13497         this.queryDelay = Math.max(this.queryDelay || 10,
13498                 this.mode == 'local' ? 10 : 250);
13499         
13500         
13501         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13502         
13503         if(this.typeAhead){
13504             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13505         }
13506         
13507         if(this.editable !== false){
13508             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13509         }
13510         
13511         this.indicator = this.indicatorEl();
13512         
13513         if(this.indicator){
13514             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13515             this.indicator.hide();
13516         }
13517         
13518     },
13519
13520     onDestroy : function(){
13521         if(this.view){
13522             this.view.setStore(null);
13523             this.view.el.removeAllListeners();
13524             this.view.el.remove();
13525             this.view.purgeListeners();
13526         }
13527         if(this.list){
13528             this.list.dom.innerHTML  = '';
13529         }
13530         
13531         if(this.store){
13532             this.store.un('beforeload', this.onBeforeLoad, this);
13533             this.store.un('load', this.onLoad, this);
13534             this.store.un('loadexception', this.onLoadException, this);
13535         }
13536         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13537     },
13538
13539     // private
13540     fireKey : function(e){
13541         if(e.isNavKeyPress() && !this.list.isVisible()){
13542             this.fireEvent("specialkey", this, e);
13543         }
13544     },
13545
13546     // private
13547     onResize: function(w, h){
13548 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13549 //        
13550 //        if(typeof w != 'number'){
13551 //            // we do not handle it!?!?
13552 //            return;
13553 //        }
13554 //        var tw = this.trigger.getWidth();
13555 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13556 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13557 //        var x = w - tw;
13558 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13559 //            
13560 //        //this.trigger.setStyle('left', x+'px');
13561 //        
13562 //        if(this.list && this.listWidth === undefined){
13563 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13564 //            this.list.setWidth(lw);
13565 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13566 //        }
13567         
13568     
13569         
13570     },
13571
13572     /**
13573      * Allow or prevent the user from directly editing the field text.  If false is passed,
13574      * the user will only be able to select from the items defined in the dropdown list.  This method
13575      * is the runtime equivalent of setting the 'editable' config option at config time.
13576      * @param {Boolean} value True to allow the user to directly edit the field text
13577      */
13578     setEditable : function(value){
13579         if(value == this.editable){
13580             return;
13581         }
13582         this.editable = value;
13583         if(!value){
13584             this.inputEl().dom.setAttribute('readOnly', true);
13585             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13586             this.inputEl().addClass('x-combo-noedit');
13587         }else{
13588             this.inputEl().dom.setAttribute('readOnly', false);
13589             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13590             this.inputEl().removeClass('x-combo-noedit');
13591         }
13592     },
13593
13594     // private
13595     
13596     onBeforeLoad : function(combo,opts){
13597         if(!this.hasFocus){
13598             return;
13599         }
13600          if (!opts.add) {
13601             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13602          }
13603         this.restrictHeight();
13604         this.selectedIndex = -1;
13605     },
13606
13607     // private
13608     onLoad : function(){
13609         
13610         this.hasQuery = false;
13611         
13612         if(!this.hasFocus){
13613             return;
13614         }
13615         
13616         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13617             this.loading.hide();
13618         }
13619         
13620         if(this.store.getCount() > 0){
13621             
13622             this.expand();
13623             this.restrictHeight();
13624             if(this.lastQuery == this.allQuery){
13625                 if(this.editable && !this.tickable){
13626                     this.inputEl().dom.select();
13627                 }
13628                 
13629                 if(
13630                     !this.selectByValue(this.value, true) &&
13631                     this.autoFocus && 
13632                     (
13633                         !this.store.lastOptions ||
13634                         typeof(this.store.lastOptions.add) == 'undefined' || 
13635                         this.store.lastOptions.add != true
13636                     )
13637                 ){
13638                     this.select(0, true);
13639                 }
13640             }else{
13641                 if(this.autoFocus){
13642                     this.selectNext();
13643                 }
13644                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13645                     this.taTask.delay(this.typeAheadDelay);
13646                 }
13647             }
13648         }else{
13649             this.onEmptyResults();
13650         }
13651         
13652         //this.el.focus();
13653     },
13654     // private
13655     onLoadException : function()
13656     {
13657         this.hasQuery = false;
13658         
13659         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13660             this.loading.hide();
13661         }
13662         
13663         if(this.tickable && this.editable){
13664             return;
13665         }
13666         
13667         this.collapse();
13668         // only causes errors at present
13669         //Roo.log(this.store.reader.jsonData);
13670         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13671             // fixme
13672             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13673         //}
13674         
13675         
13676     },
13677     // private
13678     onTypeAhead : function(){
13679         if(this.store.getCount() > 0){
13680             var r = this.store.getAt(0);
13681             var newValue = r.data[this.displayField];
13682             var len = newValue.length;
13683             var selStart = this.getRawValue().length;
13684             
13685             if(selStart != len){
13686                 this.setRawValue(newValue);
13687                 this.selectText(selStart, newValue.length);
13688             }
13689         }
13690     },
13691
13692     // private
13693     onSelect : function(record, index){
13694         
13695         if(this.fireEvent('beforeselect', this, record, index) !== false){
13696         
13697             this.setFromData(index > -1 ? record.data : false);
13698             
13699             this.collapse();
13700             this.fireEvent('select', this, record, index);
13701         }
13702     },
13703
13704     /**
13705      * Returns the currently selected field value or empty string if no value is set.
13706      * @return {String} value The selected value
13707      */
13708     getValue : function()
13709     {
13710         if(Roo.isIOS && this.useNativeIOS){
13711             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13712         }
13713         
13714         if(this.multiple){
13715             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13716         }
13717         
13718         if(this.valueField){
13719             return typeof this.value != 'undefined' ? this.value : '';
13720         }else{
13721             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13722         }
13723     },
13724     
13725     getRawValue : function()
13726     {
13727         if(Roo.isIOS && this.useNativeIOS){
13728             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13729         }
13730         
13731         var v = this.inputEl().getValue();
13732         
13733         return v;
13734     },
13735
13736     /**
13737      * Clears any text/value currently set in the field
13738      */
13739     clearValue : function(){
13740         
13741         if(this.hiddenField){
13742             this.hiddenField.dom.value = '';
13743         }
13744         this.value = '';
13745         this.setRawValue('');
13746         this.lastSelectionText = '';
13747         this.lastData = false;
13748         
13749         var close = this.closeTriggerEl();
13750         
13751         if(close){
13752             close.hide();
13753         }
13754         
13755         this.validate();
13756         
13757     },
13758
13759     /**
13760      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13761      * will be displayed in the field.  If the value does not match the data value of an existing item,
13762      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13763      * Otherwise the field will be blank (although the value will still be set).
13764      * @param {String} value The value to match
13765      */
13766     setValue : function(v)
13767     {
13768         if(Roo.isIOS && this.useNativeIOS){
13769             this.setIOSValue(v);
13770             return;
13771         }
13772         
13773         if(this.multiple){
13774             this.syncValue();
13775             return;
13776         }
13777         
13778         var text = v;
13779         if(this.valueField){
13780             var r = this.findRecord(this.valueField, v);
13781             if(r){
13782                 text = r.data[this.displayField];
13783             }else if(this.valueNotFoundText !== undefined){
13784                 text = this.valueNotFoundText;
13785             }
13786         }
13787         this.lastSelectionText = text;
13788         if(this.hiddenField){
13789             this.hiddenField.dom.value = v;
13790         }
13791         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13792         this.value = v;
13793         
13794         var close = this.closeTriggerEl();
13795         
13796         if(close){
13797             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13798         }
13799         
13800         this.validate();
13801     },
13802     /**
13803      * @property {Object} the last set data for the element
13804      */
13805     
13806     lastData : false,
13807     /**
13808      * Sets the value of the field based on a object which is related to the record format for the store.
13809      * @param {Object} value the value to set as. or false on reset?
13810      */
13811     setFromData : function(o){
13812         
13813         if(this.multiple){
13814             this.addItem(o);
13815             return;
13816         }
13817             
13818         var dv = ''; // display value
13819         var vv = ''; // value value..
13820         this.lastData = o;
13821         if (this.displayField) {
13822             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13823         } else {
13824             // this is an error condition!!!
13825             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13826         }
13827         
13828         if(this.valueField){
13829             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13830         }
13831         
13832         var close = this.closeTriggerEl();
13833         
13834         if(close){
13835             if(dv.length || vv * 1 > 0){
13836                 close.show() ;
13837                 this.blockFocus=true;
13838             } else {
13839                 close.hide();
13840             }             
13841         }
13842         
13843         if(this.hiddenField){
13844             this.hiddenField.dom.value = vv;
13845             
13846             this.lastSelectionText = dv;
13847             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13848             this.value = vv;
13849             return;
13850         }
13851         // no hidden field.. - we store the value in 'value', but still display
13852         // display field!!!!
13853         this.lastSelectionText = dv;
13854         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13855         this.value = vv;
13856         
13857         
13858         
13859     },
13860     // private
13861     reset : function(){
13862         // overridden so that last data is reset..
13863         
13864         if(this.multiple){
13865             this.clearItem();
13866             return;
13867         }
13868         
13869         this.setValue(this.originalValue);
13870         //this.clearInvalid();
13871         this.lastData = false;
13872         if (this.view) {
13873             this.view.clearSelections();
13874         }
13875         
13876         this.validate();
13877     },
13878     // private
13879     findRecord : function(prop, value){
13880         var record;
13881         if(this.store.getCount() > 0){
13882             this.store.each(function(r){
13883                 if(r.data[prop] == value){
13884                     record = r;
13885                     return false;
13886                 }
13887                 return true;
13888             });
13889         }
13890         return record;
13891     },
13892     
13893     getName: function()
13894     {
13895         // returns hidden if it's set..
13896         if (!this.rendered) {return ''};
13897         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13898         
13899     },
13900     // private
13901     onViewMove : function(e, t){
13902         this.inKeyMode = false;
13903     },
13904
13905     // private
13906     onViewOver : function(e, t){
13907         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13908             return;
13909         }
13910         var item = this.view.findItemFromChild(t);
13911         
13912         if(item){
13913             var index = this.view.indexOf(item);
13914             this.select(index, false);
13915         }
13916     },
13917
13918     // private
13919     onViewClick : function(view, doFocus, el, e)
13920     {
13921         var index = this.view.getSelectedIndexes()[0];
13922         
13923         var r = this.store.getAt(index);
13924         
13925         if(this.tickable){
13926             
13927             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13928                 return;
13929             }
13930             
13931             var rm = false;
13932             var _this = this;
13933             
13934             Roo.each(this.tickItems, function(v,k){
13935                 
13936                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13937                     Roo.log(v);
13938                     _this.tickItems.splice(k, 1);
13939                     
13940                     if(typeof(e) == 'undefined' && view == false){
13941                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13942                     }
13943                     
13944                     rm = true;
13945                     return;
13946                 }
13947             });
13948             
13949             if(rm){
13950                 return;
13951             }
13952             
13953             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13954                 this.tickItems.push(r.data);
13955             }
13956             
13957             if(typeof(e) == 'undefined' && view == false){
13958                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13959             }
13960                     
13961             return;
13962         }
13963         
13964         if(r){
13965             this.onSelect(r, index);
13966         }
13967         if(doFocus !== false && !this.blockFocus){
13968             this.inputEl().focus();
13969         }
13970     },
13971
13972     // private
13973     restrictHeight : function(){
13974         //this.innerList.dom.style.height = '';
13975         //var inner = this.innerList.dom;
13976         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13977         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13978         //this.list.beginUpdate();
13979         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13980         this.list.alignTo(this.inputEl(), this.listAlign);
13981         this.list.alignTo(this.inputEl(), this.listAlign);
13982         //this.list.endUpdate();
13983     },
13984
13985     // private
13986     onEmptyResults : function(){
13987         
13988         if(this.tickable && this.editable){
13989             this.hasFocus = false;
13990             this.restrictHeight();
13991             return;
13992         }
13993         
13994         this.collapse();
13995     },
13996
13997     /**
13998      * Returns true if the dropdown list is expanded, else false.
13999      */
14000     isExpanded : function(){
14001         return this.list.isVisible();
14002     },
14003
14004     /**
14005      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14006      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14007      * @param {String} value The data value of the item to select
14008      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14009      * selected item if it is not currently in view (defaults to true)
14010      * @return {Boolean} True if the value matched an item in the list, else false
14011      */
14012     selectByValue : function(v, scrollIntoView){
14013         if(v !== undefined && v !== null){
14014             var r = this.findRecord(this.valueField || this.displayField, v);
14015             if(r){
14016                 this.select(this.store.indexOf(r), scrollIntoView);
14017                 return true;
14018             }
14019         }
14020         return false;
14021     },
14022
14023     /**
14024      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14025      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14026      * @param {Number} index The zero-based index of the list item to select
14027      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14028      * selected item if it is not currently in view (defaults to true)
14029      */
14030     select : function(index, scrollIntoView){
14031         this.selectedIndex = index;
14032         this.view.select(index);
14033         if(scrollIntoView !== false){
14034             var el = this.view.getNode(index);
14035             /*
14036              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14037              */
14038             if(el){
14039                 this.list.scrollChildIntoView(el, false);
14040             }
14041         }
14042     },
14043
14044     // private
14045     selectNext : function(){
14046         var ct = this.store.getCount();
14047         if(ct > 0){
14048             if(this.selectedIndex == -1){
14049                 this.select(0);
14050             }else if(this.selectedIndex < ct-1){
14051                 this.select(this.selectedIndex+1);
14052             }
14053         }
14054     },
14055
14056     // private
14057     selectPrev : function(){
14058         var ct = this.store.getCount();
14059         if(ct > 0){
14060             if(this.selectedIndex == -1){
14061                 this.select(0);
14062             }else if(this.selectedIndex != 0){
14063                 this.select(this.selectedIndex-1);
14064             }
14065         }
14066     },
14067
14068     // private
14069     onKeyUp : function(e){
14070         if(this.editable !== false && !e.isSpecialKey()){
14071             this.lastKey = e.getKey();
14072             this.dqTask.delay(this.queryDelay);
14073         }
14074     },
14075
14076     // private
14077     validateBlur : function(){
14078         return !this.list || !this.list.isVisible();   
14079     },
14080
14081     // private
14082     initQuery : function(){
14083         
14084         var v = this.getRawValue();
14085         
14086         if(this.tickable && this.editable){
14087             v = this.tickableInputEl().getValue();
14088         }
14089         
14090         this.doQuery(v);
14091     },
14092
14093     // private
14094     doForce : function(){
14095         if(this.inputEl().dom.value.length > 0){
14096             this.inputEl().dom.value =
14097                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14098              
14099         }
14100     },
14101
14102     /**
14103      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14104      * query allowing the query action to be canceled if needed.
14105      * @param {String} query The SQL query to execute
14106      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14107      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14108      * saved in the current store (defaults to false)
14109      */
14110     doQuery : function(q, forceAll){
14111         
14112         if(q === undefined || q === null){
14113             q = '';
14114         }
14115         var qe = {
14116             query: q,
14117             forceAll: forceAll,
14118             combo: this,
14119             cancel:false
14120         };
14121         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14122             return false;
14123         }
14124         q = qe.query;
14125         
14126         forceAll = qe.forceAll;
14127         if(forceAll === true || (q.length >= this.minChars)){
14128             
14129             this.hasQuery = true;
14130             
14131             if(this.lastQuery != q || this.alwaysQuery){
14132                 this.lastQuery = q;
14133                 if(this.mode == 'local'){
14134                     this.selectedIndex = -1;
14135                     if(forceAll){
14136                         this.store.clearFilter();
14137                     }else{
14138                         
14139                         if(this.specialFilter){
14140                             this.fireEvent('specialfilter', this);
14141                             this.onLoad();
14142                             return;
14143                         }
14144                         
14145                         this.store.filter(this.displayField, q);
14146                     }
14147                     
14148                     this.store.fireEvent("datachanged", this.store);
14149                     
14150                     this.onLoad();
14151                     
14152                     
14153                 }else{
14154                     
14155                     this.store.baseParams[this.queryParam] = q;
14156                     
14157                     var options = {params : this.getParams(q)};
14158                     
14159                     if(this.loadNext){
14160                         options.add = true;
14161                         options.params.start = this.page * this.pageSize;
14162                     }
14163                     
14164                     this.store.load(options);
14165                     
14166                     /*
14167                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14168                      *  we should expand the list on onLoad
14169                      *  so command out it
14170                      */
14171 //                    this.expand();
14172                 }
14173             }else{
14174                 this.selectedIndex = -1;
14175                 this.onLoad();   
14176             }
14177         }
14178         
14179         this.loadNext = false;
14180     },
14181     
14182     // private
14183     getParams : function(q){
14184         var p = {};
14185         //p[this.queryParam] = q;
14186         
14187         if(this.pageSize){
14188             p.start = 0;
14189             p.limit = this.pageSize;
14190         }
14191         return p;
14192     },
14193
14194     /**
14195      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14196      */
14197     collapse : function(){
14198         if(!this.isExpanded()){
14199             return;
14200         }
14201         
14202         this.list.hide();
14203         
14204         this.hasFocus = false;
14205         
14206         if(this.tickable){
14207             this.okBtn.hide();
14208             this.cancelBtn.hide();
14209             this.trigger.show();
14210             
14211             if(this.editable){
14212                 this.tickableInputEl().dom.value = '';
14213                 this.tickableInputEl().blur();
14214             }
14215             
14216         }
14217         
14218         Roo.get(document).un('mousedown', this.collapseIf, this);
14219         Roo.get(document).un('mousewheel', this.collapseIf, this);
14220         if (!this.editable) {
14221             Roo.get(document).un('keydown', this.listKeyPress, this);
14222         }
14223         this.fireEvent('collapse', this);
14224         
14225         this.validate();
14226     },
14227
14228     // private
14229     collapseIf : function(e){
14230         var in_combo  = e.within(this.el);
14231         var in_list =  e.within(this.list);
14232         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14233         
14234         if (in_combo || in_list || is_list) {
14235             //e.stopPropagation();
14236             return;
14237         }
14238         
14239         if(this.tickable){
14240             this.onTickableFooterButtonClick(e, false, false);
14241         }
14242
14243         this.collapse();
14244         
14245     },
14246
14247     /**
14248      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14249      */
14250     expand : function(){
14251        
14252         if(this.isExpanded() || !this.hasFocus){
14253             return;
14254         }
14255         
14256         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14257         this.list.setWidth(lw);
14258         
14259         Roo.log('expand');
14260         
14261         this.list.show();
14262         
14263         this.restrictHeight();
14264         
14265         if(this.tickable){
14266             
14267             this.tickItems = Roo.apply([], this.item);
14268             
14269             this.okBtn.show();
14270             this.cancelBtn.show();
14271             this.trigger.hide();
14272             
14273             if(this.editable){
14274                 this.tickableInputEl().focus();
14275             }
14276             
14277         }
14278         
14279         Roo.get(document).on('mousedown', this.collapseIf, this);
14280         Roo.get(document).on('mousewheel', this.collapseIf, this);
14281         if (!this.editable) {
14282             Roo.get(document).on('keydown', this.listKeyPress, this);
14283         }
14284         
14285         this.fireEvent('expand', this);
14286     },
14287
14288     // private
14289     // Implements the default empty TriggerField.onTriggerClick function
14290     onTriggerClick : function(e)
14291     {
14292         Roo.log('trigger click');
14293         
14294         if(this.disabled || !this.triggerList){
14295             return;
14296         }
14297         
14298         this.page = 0;
14299         this.loadNext = false;
14300         
14301         if(this.isExpanded()){
14302             this.collapse();
14303             if (!this.blockFocus) {
14304                 this.inputEl().focus();
14305             }
14306             
14307         }else {
14308             this.hasFocus = true;
14309             if(this.triggerAction == 'all') {
14310                 this.doQuery(this.allQuery, true);
14311             } else {
14312                 this.doQuery(this.getRawValue());
14313             }
14314             if (!this.blockFocus) {
14315                 this.inputEl().focus();
14316             }
14317         }
14318     },
14319     
14320     onTickableTriggerClick : function(e)
14321     {
14322         if(this.disabled){
14323             return;
14324         }
14325         
14326         this.page = 0;
14327         this.loadNext = false;
14328         this.hasFocus = true;
14329         
14330         if(this.triggerAction == 'all') {
14331             this.doQuery(this.allQuery, true);
14332         } else {
14333             this.doQuery(this.getRawValue());
14334         }
14335     },
14336     
14337     onSearchFieldClick : function(e)
14338     {
14339         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14340             this.onTickableFooterButtonClick(e, false, false);
14341             return;
14342         }
14343         
14344         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14345             return;
14346         }
14347         
14348         this.page = 0;
14349         this.loadNext = false;
14350         this.hasFocus = true;
14351         
14352         if(this.triggerAction == 'all') {
14353             this.doQuery(this.allQuery, true);
14354         } else {
14355             this.doQuery(this.getRawValue());
14356         }
14357     },
14358     
14359     listKeyPress : function(e)
14360     {
14361         //Roo.log('listkeypress');
14362         // scroll to first matching element based on key pres..
14363         if (e.isSpecialKey()) {
14364             return false;
14365         }
14366         var k = String.fromCharCode(e.getKey()).toUpperCase();
14367         //Roo.log(k);
14368         var match  = false;
14369         var csel = this.view.getSelectedNodes();
14370         var cselitem = false;
14371         if (csel.length) {
14372             var ix = this.view.indexOf(csel[0]);
14373             cselitem  = this.store.getAt(ix);
14374             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14375                 cselitem = false;
14376             }
14377             
14378         }
14379         
14380         this.store.each(function(v) { 
14381             if (cselitem) {
14382                 // start at existing selection.
14383                 if (cselitem.id == v.id) {
14384                     cselitem = false;
14385                 }
14386                 return true;
14387             }
14388                 
14389             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14390                 match = this.store.indexOf(v);
14391                 return false;
14392             }
14393             return true;
14394         }, this);
14395         
14396         if (match === false) {
14397             return true; // no more action?
14398         }
14399         // scroll to?
14400         this.view.select(match);
14401         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14402         sn.scrollIntoView(sn.dom.parentNode, false);
14403     },
14404     
14405     onViewScroll : function(e, t){
14406         
14407         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
14408             return;
14409         }
14410         
14411         this.hasQuery = true;
14412         
14413         this.loading = this.list.select('.loading', true).first();
14414         
14415         if(this.loading === null){
14416             this.list.createChild({
14417                 tag: 'div',
14418                 cls: 'loading roo-select2-more-results roo-select2-active',
14419                 html: 'Loading more results...'
14420             });
14421             
14422             this.loading = this.list.select('.loading', true).first();
14423             
14424             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14425             
14426             this.loading.hide();
14427         }
14428         
14429         this.loading.show();
14430         
14431         var _combo = this;
14432         
14433         this.page++;
14434         this.loadNext = true;
14435         
14436         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14437         
14438         return;
14439     },
14440     
14441     addItem : function(o)
14442     {   
14443         var dv = ''; // display value
14444         
14445         if (this.displayField) {
14446             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14447         } else {
14448             // this is an error condition!!!
14449             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14450         }
14451         
14452         if(!dv.length){
14453             return;
14454         }
14455         
14456         var choice = this.choices.createChild({
14457             tag: 'li',
14458             cls: 'roo-select2-search-choice',
14459             cn: [
14460                 {
14461                     tag: 'div',
14462                     html: dv
14463                 },
14464                 {
14465                     tag: 'a',
14466                     href: '#',
14467                     cls: 'roo-select2-search-choice-close fa fa-times',
14468                     tabindex: '-1'
14469                 }
14470             ]
14471             
14472         }, this.searchField);
14473         
14474         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14475         
14476         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14477         
14478         this.item.push(o);
14479         
14480         this.lastData = o;
14481         
14482         this.syncValue();
14483         
14484         this.inputEl().dom.value = '';
14485         
14486         this.validate();
14487     },
14488     
14489     onRemoveItem : function(e, _self, o)
14490     {
14491         e.preventDefault();
14492         
14493         this.lastItem = Roo.apply([], this.item);
14494         
14495         var index = this.item.indexOf(o.data) * 1;
14496         
14497         if( index < 0){
14498             Roo.log('not this item?!');
14499             return;
14500         }
14501         
14502         this.item.splice(index, 1);
14503         o.item.remove();
14504         
14505         this.syncValue();
14506         
14507         this.fireEvent('remove', this, e);
14508         
14509         this.validate();
14510         
14511     },
14512     
14513     syncValue : function()
14514     {
14515         if(!this.item.length){
14516             this.clearValue();
14517             return;
14518         }
14519             
14520         var value = [];
14521         var _this = this;
14522         Roo.each(this.item, function(i){
14523             if(_this.valueField){
14524                 value.push(i[_this.valueField]);
14525                 return;
14526             }
14527
14528             value.push(i);
14529         });
14530
14531         this.value = value.join(',');
14532
14533         if(this.hiddenField){
14534             this.hiddenField.dom.value = this.value;
14535         }
14536         
14537         this.store.fireEvent("datachanged", this.store);
14538         
14539         this.validate();
14540     },
14541     
14542     clearItem : function()
14543     {
14544         if(!this.multiple){
14545             return;
14546         }
14547         
14548         this.item = [];
14549         
14550         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14551            c.remove();
14552         });
14553         
14554         this.syncValue();
14555         
14556         this.validate();
14557         
14558         if(this.tickable && !Roo.isTouch){
14559             this.view.refresh();
14560         }
14561     },
14562     
14563     inputEl: function ()
14564     {
14565         if(Roo.isIOS && this.useNativeIOS){
14566             return this.el.select('select.roo-ios-select', true).first();
14567         }
14568         
14569         if(Roo.isTouch && this.mobileTouchView){
14570             return this.el.select('input.form-control',true).first();
14571         }
14572         
14573         if(this.tickable){
14574             return this.searchField;
14575         }
14576         
14577         return this.el.select('input.form-control',true).first();
14578     },
14579     
14580     onTickableFooterButtonClick : function(e, btn, el)
14581     {
14582         e.preventDefault();
14583         
14584         this.lastItem = Roo.apply([], this.item);
14585         
14586         if(btn && btn.name == 'cancel'){
14587             this.tickItems = Roo.apply([], this.item);
14588             this.collapse();
14589             return;
14590         }
14591         
14592         this.clearItem();
14593         
14594         var _this = this;
14595         
14596         Roo.each(this.tickItems, function(o){
14597             _this.addItem(o);
14598         });
14599         
14600         this.collapse();
14601         
14602     },
14603     
14604     validate : function()
14605     {
14606         var v = this.getRawValue();
14607         
14608         if(this.multiple){
14609             v = this.getValue();
14610         }
14611         
14612         if(this.disabled || this.allowBlank || v.length){
14613             this.markValid();
14614             return true;
14615         }
14616         
14617         this.markInvalid();
14618         return false;
14619     },
14620     
14621     tickableInputEl : function()
14622     {
14623         if(!this.tickable || !this.editable){
14624             return this.inputEl();
14625         }
14626         
14627         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14628     },
14629     
14630     
14631     getAutoCreateTouchView : function()
14632     {
14633         var id = Roo.id();
14634         
14635         var cfg = {
14636             cls: 'form-group' //input-group
14637         };
14638         
14639         var input =  {
14640             tag: 'input',
14641             id : id,
14642             type : this.inputType,
14643             cls : 'form-control x-combo-noedit',
14644             autocomplete: 'new-password',
14645             placeholder : this.placeholder || '',
14646             readonly : true
14647         };
14648         
14649         if (this.name) {
14650             input.name = this.name;
14651         }
14652         
14653         if (this.size) {
14654             input.cls += ' input-' + this.size;
14655         }
14656         
14657         if (this.disabled) {
14658             input.disabled = true;
14659         }
14660         
14661         var inputblock = {
14662             cls : '',
14663             cn : [
14664                 input
14665             ]
14666         };
14667         
14668         if(this.before){
14669             inputblock.cls += ' input-group';
14670             
14671             inputblock.cn.unshift({
14672                 tag :'span',
14673                 cls : 'input-group-addon',
14674                 html : this.before
14675             });
14676         }
14677         
14678         if(this.removable && !this.multiple){
14679             inputblock.cls += ' roo-removable';
14680             
14681             inputblock.cn.push({
14682                 tag: 'button',
14683                 html : 'x',
14684                 cls : 'roo-combo-removable-btn close'
14685             });
14686         }
14687
14688         if(this.hasFeedback && !this.allowBlank){
14689             
14690             inputblock.cls += ' has-feedback';
14691             
14692             inputblock.cn.push({
14693                 tag: 'span',
14694                 cls: 'glyphicon form-control-feedback'
14695             });
14696             
14697         }
14698         
14699         if (this.after) {
14700             
14701             inputblock.cls += (this.before) ? '' : ' input-group';
14702             
14703             inputblock.cn.push({
14704                 tag :'span',
14705                 cls : 'input-group-addon',
14706                 html : this.after
14707             });
14708         }
14709
14710         var box = {
14711             tag: 'div',
14712             cn: [
14713                 {
14714                     tag: 'input',
14715                     type : 'hidden',
14716                     cls: 'form-hidden-field'
14717                 },
14718                 inputblock
14719             ]
14720             
14721         };
14722         
14723         if(this.multiple){
14724             box = {
14725                 tag: 'div',
14726                 cn: [
14727                     {
14728                         tag: 'input',
14729                         type : 'hidden',
14730                         cls: 'form-hidden-field'
14731                     },
14732                     {
14733                         tag: 'ul',
14734                         cls: 'roo-select2-choices',
14735                         cn:[
14736                             {
14737                                 tag: 'li',
14738                                 cls: 'roo-select2-search-field',
14739                                 cn: [
14740
14741                                     inputblock
14742                                 ]
14743                             }
14744                         ]
14745                     }
14746                 ]
14747             }
14748         };
14749         
14750         var combobox = {
14751             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14752             cn: [
14753                 box
14754             ]
14755         };
14756         
14757         if(!this.multiple && this.showToggleBtn){
14758             
14759             var caret = {
14760                         tag: 'span',
14761                         cls: 'caret'
14762             };
14763             
14764             if (this.caret != false) {
14765                 caret = {
14766                      tag: 'i',
14767                      cls: 'fa fa-' + this.caret
14768                 };
14769                 
14770             }
14771             
14772             combobox.cn.push({
14773                 tag :'span',
14774                 cls : 'input-group-addon btn dropdown-toggle',
14775                 cn : [
14776                     caret,
14777                     {
14778                         tag: 'span',
14779                         cls: 'combobox-clear',
14780                         cn  : [
14781                             {
14782                                 tag : 'i',
14783                                 cls: 'icon-remove'
14784                             }
14785                         ]
14786                     }
14787                 ]
14788
14789             })
14790         }
14791         
14792         if(this.multiple){
14793             combobox.cls += ' roo-select2-container-multi';
14794         }
14795         
14796         var align = this.labelAlign || this.parentLabelAlign();
14797         
14798         if (align ==='left' && this.fieldLabel.length) {
14799
14800             cfg.cn = [
14801                 {
14802                    tag : 'i',
14803                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14804                    tooltip : 'This field is required'
14805                 },
14806                 {
14807                     tag: 'label',
14808                     cls : 'control-label',
14809                     html : this.fieldLabel
14810
14811                 },
14812                 {
14813                     cls : '', 
14814                     cn: [
14815                         combobox
14816                     ]
14817                 }
14818             ];
14819             
14820             var labelCfg = cfg.cn[1];
14821             var contentCfg = cfg.cn[2];
14822             
14823
14824             if(this.indicatorpos == 'right'){
14825                 cfg.cn = [
14826                     {
14827                         tag: 'label',
14828                         'for' :  id,
14829                         cls : 'control-label',
14830                         cn : [
14831                             {
14832                                 tag : 'span',
14833                                 html : this.fieldLabel
14834                             },
14835                             {
14836                                 tag : 'i',
14837                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14838                                 tooltip : 'This field is required'
14839                             }
14840                         ]
14841                     },
14842                     {
14843                         cls : "",
14844                         cn: [
14845                             combobox
14846                         ]
14847                     }
14848
14849                 ];
14850                 
14851                 labelCfg = cfg.cn[0];
14852                 contentCfg = cfg.cn[1];
14853             }
14854             
14855            
14856             
14857             if(this.labelWidth > 12){
14858                 labelCfg.style = "width: " + this.labelWidth + 'px';
14859             }
14860             
14861             if(this.labelWidth < 13 && this.labelmd == 0){
14862                 this.labelmd = this.labelWidth;
14863             }
14864             
14865             if(this.labellg > 0){
14866                 labelCfg.cls += ' col-lg-' + this.labellg;
14867                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14868             }
14869             
14870             if(this.labelmd > 0){
14871                 labelCfg.cls += ' col-md-' + this.labelmd;
14872                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14873             }
14874             
14875             if(this.labelsm > 0){
14876                 labelCfg.cls += ' col-sm-' + this.labelsm;
14877                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14878             }
14879             
14880             if(this.labelxs > 0){
14881                 labelCfg.cls += ' col-xs-' + this.labelxs;
14882                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14883             }
14884                 
14885                 
14886         } else if ( this.fieldLabel.length) {
14887             cfg.cn = [
14888                 {
14889                    tag : 'i',
14890                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14891                    tooltip : 'This field is required'
14892                 },
14893                 {
14894                     tag: 'label',
14895                     cls : 'control-label',
14896                     html : this.fieldLabel
14897
14898                 },
14899                 {
14900                     cls : '', 
14901                     cn: [
14902                         combobox
14903                     ]
14904                 }
14905             ];
14906             
14907             if(this.indicatorpos == 'right'){
14908                 cfg.cn = [
14909                     {
14910                         tag: 'label',
14911                         cls : 'control-label',
14912                         html : this.fieldLabel,
14913                         cn : [
14914                             {
14915                                tag : 'i',
14916                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14917                                tooltip : 'This field is required'
14918                             }
14919                         ]
14920                     },
14921                     {
14922                         cls : '', 
14923                         cn: [
14924                             combobox
14925                         ]
14926                     }
14927                 ];
14928             }
14929         } else {
14930             cfg.cn = combobox;    
14931         }
14932         
14933         
14934         var settings = this;
14935         
14936         ['xs','sm','md','lg'].map(function(size){
14937             if (settings[size]) {
14938                 cfg.cls += ' col-' + size + '-' + settings[size];
14939             }
14940         });
14941         
14942         return cfg;
14943     },
14944     
14945     initTouchView : function()
14946     {
14947         this.renderTouchView();
14948         
14949         this.touchViewEl.on('scroll', function(){
14950             this.el.dom.scrollTop = 0;
14951         }, this);
14952         
14953         this.originalValue = this.getValue();
14954         
14955         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14956         
14957         this.inputEl().on("click", this.showTouchView, this);
14958         if (this.triggerEl) {
14959             this.triggerEl.on("click", this.showTouchView, this);
14960         }
14961         
14962         
14963         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14964         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14965         
14966         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14967         
14968         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14969         this.store.on('load', this.onTouchViewLoad, this);
14970         this.store.on('loadexception', this.onTouchViewLoadException, this);
14971         
14972         if(this.hiddenName){
14973             
14974             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14975             
14976             this.hiddenField.dom.value =
14977                 this.hiddenValue !== undefined ? this.hiddenValue :
14978                 this.value !== undefined ? this.value : '';
14979         
14980             this.el.dom.removeAttribute('name');
14981             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14982         }
14983         
14984         if(this.multiple){
14985             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14986             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14987         }
14988         
14989         if(this.removable && !this.multiple){
14990             var close = this.closeTriggerEl();
14991             if(close){
14992                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14993                 close.on('click', this.removeBtnClick, this, close);
14994             }
14995         }
14996         /*
14997          * fix the bug in Safari iOS8
14998          */
14999         this.inputEl().on("focus", function(e){
15000             document.activeElement.blur();
15001         }, this);
15002         
15003         return;
15004         
15005         
15006     },
15007     
15008     renderTouchView : function()
15009     {
15010         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15011         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15012         
15013         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15014         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15015         
15016         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15017         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15018         this.touchViewBodyEl.setStyle('overflow', 'auto');
15019         
15020         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15021         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15022         
15023         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15024         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15025         
15026     },
15027     
15028     showTouchView : function()
15029     {
15030         if(this.disabled){
15031             return;
15032         }
15033         
15034         this.touchViewHeaderEl.hide();
15035
15036         if(this.modalTitle.length){
15037             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15038             this.touchViewHeaderEl.show();
15039         }
15040
15041         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15042         this.touchViewEl.show();
15043
15044         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15045         
15046         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15047         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15048
15049         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15050
15051         if(this.modalTitle.length){
15052             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15053         }
15054         
15055         this.touchViewBodyEl.setHeight(bodyHeight);
15056
15057         if(this.animate){
15058             var _this = this;
15059             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15060         }else{
15061             this.touchViewEl.addClass('in');
15062         }
15063
15064         this.doTouchViewQuery();
15065         
15066     },
15067     
15068     hideTouchView : function()
15069     {
15070         this.touchViewEl.removeClass('in');
15071
15072         if(this.animate){
15073             var _this = this;
15074             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15075         }else{
15076             this.touchViewEl.setStyle('display', 'none');
15077         }
15078         
15079     },
15080     
15081     setTouchViewValue : function()
15082     {
15083         if(this.multiple){
15084             this.clearItem();
15085         
15086             var _this = this;
15087
15088             Roo.each(this.tickItems, function(o){
15089                 this.addItem(o);
15090             }, this);
15091         }
15092         
15093         this.hideTouchView();
15094     },
15095     
15096     doTouchViewQuery : function()
15097     {
15098         var qe = {
15099             query: '',
15100             forceAll: true,
15101             combo: this,
15102             cancel:false
15103         };
15104         
15105         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15106             return false;
15107         }
15108         
15109         if(!this.alwaysQuery || this.mode == 'local'){
15110             this.onTouchViewLoad();
15111             return;
15112         }
15113         
15114         this.store.load();
15115     },
15116     
15117     onTouchViewBeforeLoad : function(combo,opts)
15118     {
15119         return;
15120     },
15121
15122     // private
15123     onTouchViewLoad : function()
15124     {
15125         if(this.store.getCount() < 1){
15126             this.onTouchViewEmptyResults();
15127             return;
15128         }
15129         
15130         this.clearTouchView();
15131         
15132         var rawValue = this.getRawValue();
15133         
15134         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15135         
15136         this.tickItems = [];
15137         
15138         this.store.data.each(function(d, rowIndex){
15139             var row = this.touchViewListGroup.createChild(template);
15140             
15141             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15142                 row.addClass(d.data.cls);
15143             }
15144             
15145             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15146                 var cfg = {
15147                     data : d.data,
15148                     html : d.data[this.displayField]
15149                 };
15150                 
15151                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15152                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15153                 }
15154             }
15155             row.removeClass('selected');
15156             if(!this.multiple && this.valueField &&
15157                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15158             {
15159                 // radio buttons..
15160                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15161                 row.addClass('selected');
15162             }
15163             
15164             if(this.multiple && this.valueField &&
15165                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15166             {
15167                 
15168                 // checkboxes...
15169                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15170                 this.tickItems.push(d.data);
15171             }
15172             
15173             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15174             
15175         }, this);
15176         
15177         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15178         
15179         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15180
15181         if(this.modalTitle.length){
15182             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15183         }
15184
15185         var listHeight = this.touchViewListGroup.getHeight();
15186         
15187         var _this = this;
15188         
15189         if(firstChecked && listHeight > bodyHeight){
15190             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15191         }
15192         
15193     },
15194     
15195     onTouchViewLoadException : function()
15196     {
15197         this.hideTouchView();
15198     },
15199     
15200     onTouchViewEmptyResults : function()
15201     {
15202         this.clearTouchView();
15203         
15204         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15205         
15206         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15207         
15208     },
15209     
15210     clearTouchView : function()
15211     {
15212         this.touchViewListGroup.dom.innerHTML = '';
15213     },
15214     
15215     onTouchViewClick : function(e, el, o)
15216     {
15217         e.preventDefault();
15218         
15219         var row = o.row;
15220         var rowIndex = o.rowIndex;
15221         
15222         var r = this.store.getAt(rowIndex);
15223         
15224         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15225             
15226             if(!this.multiple){
15227                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15228                     c.dom.removeAttribute('checked');
15229                 }, this);
15230
15231                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15232
15233                 this.setFromData(r.data);
15234
15235                 var close = this.closeTriggerEl();
15236
15237                 if(close){
15238                     close.show();
15239                 }
15240
15241                 this.hideTouchView();
15242
15243                 this.fireEvent('select', this, r, rowIndex);
15244
15245                 return;
15246             }
15247
15248             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15249                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15250                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15251                 return;
15252             }
15253
15254             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15255             this.addItem(r.data);
15256             this.tickItems.push(r.data);
15257         }
15258     },
15259     
15260     getAutoCreateNativeIOS : function()
15261     {
15262         var cfg = {
15263             cls: 'form-group' //input-group,
15264         };
15265         
15266         var combobox =  {
15267             tag: 'select',
15268             cls : 'roo-ios-select'
15269         };
15270         
15271         if (this.name) {
15272             combobox.name = this.name;
15273         }
15274         
15275         if (this.disabled) {
15276             combobox.disabled = true;
15277         }
15278         
15279         var settings = this;
15280         
15281         ['xs','sm','md','lg'].map(function(size){
15282             if (settings[size]) {
15283                 cfg.cls += ' col-' + size + '-' + settings[size];
15284             }
15285         });
15286         
15287         cfg.cn = combobox;
15288         
15289         return cfg;
15290         
15291     },
15292     
15293     initIOSView : function()
15294     {
15295         this.store.on('load', this.onIOSViewLoad, this);
15296         
15297         return;
15298     },
15299     
15300     onIOSViewLoad : function()
15301     {
15302         if(this.store.getCount() < 1){
15303             return;
15304         }
15305         
15306         this.clearIOSView();
15307         
15308         if(this.allowBlank) {
15309             
15310             var default_text = '-- SELECT --';
15311             
15312             if(this.placeholder.length){
15313                 default_text = this.placeholder;
15314             }
15315             
15316             if(this.emptyTitle.length){
15317                 default_text += ' - ' + this.emptyTitle + ' -';
15318             }
15319             
15320             var opt = this.inputEl().createChild({
15321                 tag: 'option',
15322                 value : 0,
15323                 html : default_text
15324             });
15325             
15326             var o = {};
15327             o[this.valueField] = 0;
15328             o[this.displayField] = default_text;
15329             
15330             this.ios_options.push({
15331                 data : o,
15332                 el : opt
15333             });
15334             
15335         }
15336         
15337         this.store.data.each(function(d, rowIndex){
15338             
15339             var html = '';
15340             
15341             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15342                 html = d.data[this.displayField];
15343             }
15344             
15345             var value = '';
15346             
15347             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15348                 value = d.data[this.valueField];
15349             }
15350             
15351             var option = {
15352                 tag: 'option',
15353                 value : value,
15354                 html : html
15355             };
15356             
15357             if(this.value == d.data[this.valueField]){
15358                 option['selected'] = true;
15359             }
15360             
15361             var opt = this.inputEl().createChild(option);
15362             
15363             this.ios_options.push({
15364                 data : d.data,
15365                 el : opt
15366             });
15367             
15368         }, this);
15369         
15370         this.inputEl().on('change', function(){
15371            this.fireEvent('select', this);
15372         }, this);
15373         
15374     },
15375     
15376     clearIOSView: function()
15377     {
15378         this.inputEl().dom.innerHTML = '';
15379         
15380         this.ios_options = [];
15381     },
15382     
15383     setIOSValue: function(v)
15384     {
15385         this.value = v;
15386         
15387         if(!this.ios_options){
15388             return;
15389         }
15390         
15391         Roo.each(this.ios_options, function(opts){
15392            
15393            opts.el.dom.removeAttribute('selected');
15394            
15395            if(opts.data[this.valueField] != v){
15396                return;
15397            }
15398            
15399            opts.el.dom.setAttribute('selected', true);
15400            
15401         }, this);
15402     }
15403
15404     /** 
15405     * @cfg {Boolean} grow 
15406     * @hide 
15407     */
15408     /** 
15409     * @cfg {Number} growMin 
15410     * @hide 
15411     */
15412     /** 
15413     * @cfg {Number} growMax 
15414     * @hide 
15415     */
15416     /**
15417      * @hide
15418      * @method autoSize
15419      */
15420 });
15421
15422 Roo.apply(Roo.bootstrap.ComboBox,  {
15423     
15424     header : {
15425         tag: 'div',
15426         cls: 'modal-header',
15427         cn: [
15428             {
15429                 tag: 'h4',
15430                 cls: 'modal-title'
15431             }
15432         ]
15433     },
15434     
15435     body : {
15436         tag: 'div',
15437         cls: 'modal-body',
15438         cn: [
15439             {
15440                 tag: 'ul',
15441                 cls: 'list-group'
15442             }
15443         ]
15444     },
15445     
15446     listItemRadio : {
15447         tag: 'li',
15448         cls: 'list-group-item',
15449         cn: [
15450             {
15451                 tag: 'span',
15452                 cls: 'roo-combobox-list-group-item-value'
15453             },
15454             {
15455                 tag: 'div',
15456                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15457                 cn: [
15458                     {
15459                         tag: 'input',
15460                         type: 'radio'
15461                     },
15462                     {
15463                         tag: 'label'
15464                     }
15465                 ]
15466             }
15467         ]
15468     },
15469     
15470     listItemCheckbox : {
15471         tag: 'li',
15472         cls: 'list-group-item',
15473         cn: [
15474             {
15475                 tag: 'span',
15476                 cls: 'roo-combobox-list-group-item-value'
15477             },
15478             {
15479                 tag: 'div',
15480                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15481                 cn: [
15482                     {
15483                         tag: 'input',
15484                         type: 'checkbox'
15485                     },
15486                     {
15487                         tag: 'label'
15488                     }
15489                 ]
15490             }
15491         ]
15492     },
15493     
15494     emptyResult : {
15495         tag: 'div',
15496         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15497     },
15498     
15499     footer : {
15500         tag: 'div',
15501         cls: 'modal-footer',
15502         cn: [
15503             {
15504                 tag: 'div',
15505                 cls: 'row',
15506                 cn: [
15507                     {
15508                         tag: 'div',
15509                         cls: 'col-xs-6 text-left',
15510                         cn: {
15511                             tag: 'button',
15512                             cls: 'btn btn-danger roo-touch-view-cancel',
15513                             html: 'Cancel'
15514                         }
15515                     },
15516                     {
15517                         tag: 'div',
15518                         cls: 'col-xs-6 text-right',
15519                         cn: {
15520                             tag: 'button',
15521                             cls: 'btn btn-success roo-touch-view-ok',
15522                             html: 'OK'
15523                         }
15524                     }
15525                 ]
15526             }
15527         ]
15528         
15529     }
15530 });
15531
15532 Roo.apply(Roo.bootstrap.ComboBox,  {
15533     
15534     touchViewTemplate : {
15535         tag: 'div',
15536         cls: 'modal fade roo-combobox-touch-view',
15537         cn: [
15538             {
15539                 tag: 'div',
15540                 cls: 'modal-dialog',
15541                 style : 'position:fixed', // we have to fix position....
15542                 cn: [
15543                     {
15544                         tag: 'div',
15545                         cls: 'modal-content',
15546                         cn: [
15547                             Roo.bootstrap.ComboBox.header,
15548                             Roo.bootstrap.ComboBox.body,
15549                             Roo.bootstrap.ComboBox.footer
15550                         ]
15551                     }
15552                 ]
15553             }
15554         ]
15555     }
15556 });/*
15557  * Based on:
15558  * Ext JS Library 1.1.1
15559  * Copyright(c) 2006-2007, Ext JS, LLC.
15560  *
15561  * Originally Released Under LGPL - original licence link has changed is not relivant.
15562  *
15563  * Fork - LGPL
15564  * <script type="text/javascript">
15565  */
15566
15567 /**
15568  * @class Roo.View
15569  * @extends Roo.util.Observable
15570  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15571  * This class also supports single and multi selection modes. <br>
15572  * Create a data model bound view:
15573  <pre><code>
15574  var store = new Roo.data.Store(...);
15575
15576  var view = new Roo.View({
15577     el : "my-element",
15578     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15579  
15580     singleSelect: true,
15581     selectedClass: "ydataview-selected",
15582     store: store
15583  });
15584
15585  // listen for node click?
15586  view.on("click", function(vw, index, node, e){
15587  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15588  });
15589
15590  // load XML data
15591  dataModel.load("foobar.xml");
15592  </code></pre>
15593  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15594  * <br><br>
15595  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15596  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15597  * 
15598  * Note: old style constructor is still suported (container, template, config)
15599  * 
15600  * @constructor
15601  * Create a new View
15602  * @param {Object} config The config object
15603  * 
15604  */
15605 Roo.View = function(config, depreciated_tpl, depreciated_config){
15606     
15607     this.parent = false;
15608     
15609     if (typeof(depreciated_tpl) == 'undefined') {
15610         // new way.. - universal constructor.
15611         Roo.apply(this, config);
15612         this.el  = Roo.get(this.el);
15613     } else {
15614         // old format..
15615         this.el  = Roo.get(config);
15616         this.tpl = depreciated_tpl;
15617         Roo.apply(this, depreciated_config);
15618     }
15619     this.wrapEl  = this.el.wrap().wrap();
15620     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15621     
15622     
15623     if(typeof(this.tpl) == "string"){
15624         this.tpl = new Roo.Template(this.tpl);
15625     } else {
15626         // support xtype ctors..
15627         this.tpl = new Roo.factory(this.tpl, Roo);
15628     }
15629     
15630     
15631     this.tpl.compile();
15632     
15633     /** @private */
15634     this.addEvents({
15635         /**
15636          * @event beforeclick
15637          * Fires before a click is processed. Returns false to cancel the default action.
15638          * @param {Roo.View} this
15639          * @param {Number} index The index of the target node
15640          * @param {HTMLElement} node The target node
15641          * @param {Roo.EventObject} e The raw event object
15642          */
15643             "beforeclick" : true,
15644         /**
15645          * @event click
15646          * Fires when a template node is clicked.
15647          * @param {Roo.View} this
15648          * @param {Number} index The index of the target node
15649          * @param {HTMLElement} node The target node
15650          * @param {Roo.EventObject} e The raw event object
15651          */
15652             "click" : true,
15653         /**
15654          * @event dblclick
15655          * Fires when a template node is double clicked.
15656          * @param {Roo.View} this
15657          * @param {Number} index The index of the target node
15658          * @param {HTMLElement} node The target node
15659          * @param {Roo.EventObject} e The raw event object
15660          */
15661             "dblclick" : true,
15662         /**
15663          * @event contextmenu
15664          * Fires when a template node is right clicked.
15665          * @param {Roo.View} this
15666          * @param {Number} index The index of the target node
15667          * @param {HTMLElement} node The target node
15668          * @param {Roo.EventObject} e The raw event object
15669          */
15670             "contextmenu" : true,
15671         /**
15672          * @event selectionchange
15673          * Fires when the selected nodes change.
15674          * @param {Roo.View} this
15675          * @param {Array} selections Array of the selected nodes
15676          */
15677             "selectionchange" : true,
15678     
15679         /**
15680          * @event beforeselect
15681          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15682          * @param {Roo.View} this
15683          * @param {HTMLElement} node The node to be selected
15684          * @param {Array} selections Array of currently selected nodes
15685          */
15686             "beforeselect" : true,
15687         /**
15688          * @event preparedata
15689          * Fires on every row to render, to allow you to change the data.
15690          * @param {Roo.View} this
15691          * @param {Object} data to be rendered (change this)
15692          */
15693           "preparedata" : true
15694           
15695           
15696         });
15697
15698
15699
15700     this.el.on({
15701         "click": this.onClick,
15702         "dblclick": this.onDblClick,
15703         "contextmenu": this.onContextMenu,
15704         scope:this
15705     });
15706
15707     this.selections = [];
15708     this.nodes = [];
15709     this.cmp = new Roo.CompositeElementLite([]);
15710     if(this.store){
15711         this.store = Roo.factory(this.store, Roo.data);
15712         this.setStore(this.store, true);
15713     }
15714     
15715     if ( this.footer && this.footer.xtype) {
15716            
15717          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15718         
15719         this.footer.dataSource = this.store;
15720         this.footer.container = fctr;
15721         this.footer = Roo.factory(this.footer, Roo);
15722         fctr.insertFirst(this.el);
15723         
15724         // this is a bit insane - as the paging toolbar seems to detach the el..
15725 //        dom.parentNode.parentNode.parentNode
15726          // they get detached?
15727     }
15728     
15729     
15730     Roo.View.superclass.constructor.call(this);
15731     
15732     
15733 };
15734
15735 Roo.extend(Roo.View, Roo.util.Observable, {
15736     
15737      /**
15738      * @cfg {Roo.data.Store} store Data store to load data from.
15739      */
15740     store : false,
15741     
15742     /**
15743      * @cfg {String|Roo.Element} el The container element.
15744      */
15745     el : '',
15746     
15747     /**
15748      * @cfg {String|Roo.Template} tpl The template used by this View 
15749      */
15750     tpl : false,
15751     /**
15752      * @cfg {String} dataName the named area of the template to use as the data area
15753      *                          Works with domtemplates roo-name="name"
15754      */
15755     dataName: false,
15756     /**
15757      * @cfg {String} selectedClass The css class to add to selected nodes
15758      */
15759     selectedClass : "x-view-selected",
15760      /**
15761      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15762      */
15763     emptyText : "",
15764     
15765     /**
15766      * @cfg {String} text to display on mask (default Loading)
15767      */
15768     mask : false,
15769     /**
15770      * @cfg {Boolean} multiSelect Allow multiple selection
15771      */
15772     multiSelect : false,
15773     /**
15774      * @cfg {Boolean} singleSelect Allow single selection
15775      */
15776     singleSelect:  false,
15777     
15778     /**
15779      * @cfg {Boolean} toggleSelect - selecting 
15780      */
15781     toggleSelect : false,
15782     
15783     /**
15784      * @cfg {Boolean} tickable - selecting 
15785      */
15786     tickable : false,
15787     
15788     /**
15789      * Returns the element this view is bound to.
15790      * @return {Roo.Element}
15791      */
15792     getEl : function(){
15793         return this.wrapEl;
15794     },
15795     
15796     
15797
15798     /**
15799      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15800      */
15801     refresh : function(){
15802         //Roo.log('refresh');
15803         var t = this.tpl;
15804         
15805         // if we are using something like 'domtemplate', then
15806         // the what gets used is:
15807         // t.applySubtemplate(NAME, data, wrapping data..)
15808         // the outer template then get' applied with
15809         //     the store 'extra data'
15810         // and the body get's added to the
15811         //      roo-name="data" node?
15812         //      <span class='roo-tpl-{name}'></span> ?????
15813         
15814         
15815         
15816         this.clearSelections();
15817         this.el.update("");
15818         var html = [];
15819         var records = this.store.getRange();
15820         if(records.length < 1) {
15821             
15822             // is this valid??  = should it render a template??
15823             
15824             this.el.update(this.emptyText);
15825             return;
15826         }
15827         var el = this.el;
15828         if (this.dataName) {
15829             this.el.update(t.apply(this.store.meta)); //????
15830             el = this.el.child('.roo-tpl-' + this.dataName);
15831         }
15832         
15833         for(var i = 0, len = records.length; i < len; i++){
15834             var data = this.prepareData(records[i].data, i, records[i]);
15835             this.fireEvent("preparedata", this, data, i, records[i]);
15836             
15837             var d = Roo.apply({}, data);
15838             
15839             if(this.tickable){
15840                 Roo.apply(d, {'roo-id' : Roo.id()});
15841                 
15842                 var _this = this;
15843             
15844                 Roo.each(this.parent.item, function(item){
15845                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15846                         return;
15847                     }
15848                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15849                 });
15850             }
15851             
15852             html[html.length] = Roo.util.Format.trim(
15853                 this.dataName ?
15854                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15855                     t.apply(d)
15856             );
15857         }
15858         
15859         
15860         
15861         el.update(html.join(""));
15862         this.nodes = el.dom.childNodes;
15863         this.updateIndexes(0);
15864     },
15865     
15866
15867     /**
15868      * Function to override to reformat the data that is sent to
15869      * the template for each node.
15870      * DEPRICATED - use the preparedata event handler.
15871      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15872      * a JSON object for an UpdateManager bound view).
15873      */
15874     prepareData : function(data, index, record)
15875     {
15876         this.fireEvent("preparedata", this, data, index, record);
15877         return data;
15878     },
15879
15880     onUpdate : function(ds, record){
15881         // Roo.log('on update');   
15882         this.clearSelections();
15883         var index = this.store.indexOf(record);
15884         var n = this.nodes[index];
15885         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15886         n.parentNode.removeChild(n);
15887         this.updateIndexes(index, index);
15888     },
15889
15890     
15891     
15892 // --------- FIXME     
15893     onAdd : function(ds, records, index)
15894     {
15895         //Roo.log(['on Add', ds, records, index] );        
15896         this.clearSelections();
15897         if(this.nodes.length == 0){
15898             this.refresh();
15899             return;
15900         }
15901         var n = this.nodes[index];
15902         for(var i = 0, len = records.length; i < len; i++){
15903             var d = this.prepareData(records[i].data, i, records[i]);
15904             if(n){
15905                 this.tpl.insertBefore(n, d);
15906             }else{
15907                 
15908                 this.tpl.append(this.el, d);
15909             }
15910         }
15911         this.updateIndexes(index);
15912     },
15913
15914     onRemove : function(ds, record, index){
15915        // Roo.log('onRemove');
15916         this.clearSelections();
15917         var el = this.dataName  ?
15918             this.el.child('.roo-tpl-' + this.dataName) :
15919             this.el; 
15920         
15921         el.dom.removeChild(this.nodes[index]);
15922         this.updateIndexes(index);
15923     },
15924
15925     /**
15926      * Refresh an individual node.
15927      * @param {Number} index
15928      */
15929     refreshNode : function(index){
15930         this.onUpdate(this.store, this.store.getAt(index));
15931     },
15932
15933     updateIndexes : function(startIndex, endIndex){
15934         var ns = this.nodes;
15935         startIndex = startIndex || 0;
15936         endIndex = endIndex || ns.length - 1;
15937         for(var i = startIndex; i <= endIndex; i++){
15938             ns[i].nodeIndex = i;
15939         }
15940     },
15941
15942     /**
15943      * Changes the data store this view uses and refresh the view.
15944      * @param {Store} store
15945      */
15946     setStore : function(store, initial){
15947         if(!initial && this.store){
15948             this.store.un("datachanged", this.refresh);
15949             this.store.un("add", this.onAdd);
15950             this.store.un("remove", this.onRemove);
15951             this.store.un("update", this.onUpdate);
15952             this.store.un("clear", this.refresh);
15953             this.store.un("beforeload", this.onBeforeLoad);
15954             this.store.un("load", this.onLoad);
15955             this.store.un("loadexception", this.onLoad);
15956         }
15957         if(store){
15958           
15959             store.on("datachanged", this.refresh, this);
15960             store.on("add", this.onAdd, this);
15961             store.on("remove", this.onRemove, this);
15962             store.on("update", this.onUpdate, this);
15963             store.on("clear", this.refresh, this);
15964             store.on("beforeload", this.onBeforeLoad, this);
15965             store.on("load", this.onLoad, this);
15966             store.on("loadexception", this.onLoad, this);
15967         }
15968         
15969         if(store){
15970             this.refresh();
15971         }
15972     },
15973     /**
15974      * onbeforeLoad - masks the loading area.
15975      *
15976      */
15977     onBeforeLoad : function(store,opts)
15978     {
15979          //Roo.log('onBeforeLoad');   
15980         if (!opts.add) {
15981             this.el.update("");
15982         }
15983         this.el.mask(this.mask ? this.mask : "Loading" ); 
15984     },
15985     onLoad : function ()
15986     {
15987         this.el.unmask();
15988     },
15989     
15990
15991     /**
15992      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15993      * @param {HTMLElement} node
15994      * @return {HTMLElement} The template node
15995      */
15996     findItemFromChild : function(node){
15997         var el = this.dataName  ?
15998             this.el.child('.roo-tpl-' + this.dataName,true) :
15999             this.el.dom; 
16000         
16001         if(!node || node.parentNode == el){
16002                     return node;
16003             }
16004             var p = node.parentNode;
16005             while(p && p != el){
16006             if(p.parentNode == el){
16007                 return p;
16008             }
16009             p = p.parentNode;
16010         }
16011             return null;
16012     },
16013
16014     /** @ignore */
16015     onClick : function(e){
16016         var item = this.findItemFromChild(e.getTarget());
16017         if(item){
16018             var index = this.indexOf(item);
16019             if(this.onItemClick(item, index, e) !== false){
16020                 this.fireEvent("click", this, index, item, e);
16021             }
16022         }else{
16023             this.clearSelections();
16024         }
16025     },
16026
16027     /** @ignore */
16028     onContextMenu : function(e){
16029         var item = this.findItemFromChild(e.getTarget());
16030         if(item){
16031             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16032         }
16033     },
16034
16035     /** @ignore */
16036     onDblClick : function(e){
16037         var item = this.findItemFromChild(e.getTarget());
16038         if(item){
16039             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16040         }
16041     },
16042
16043     onItemClick : function(item, index, e)
16044     {
16045         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16046             return false;
16047         }
16048         if (this.toggleSelect) {
16049             var m = this.isSelected(item) ? 'unselect' : 'select';
16050             //Roo.log(m);
16051             var _t = this;
16052             _t[m](item, true, false);
16053             return true;
16054         }
16055         if(this.multiSelect || this.singleSelect){
16056             if(this.multiSelect && e.shiftKey && this.lastSelection){
16057                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16058             }else{
16059                 this.select(item, this.multiSelect && e.ctrlKey);
16060                 this.lastSelection = item;
16061             }
16062             
16063             if(!this.tickable){
16064                 e.preventDefault();
16065             }
16066             
16067         }
16068         return true;
16069     },
16070
16071     /**
16072      * Get the number of selected nodes.
16073      * @return {Number}
16074      */
16075     getSelectionCount : function(){
16076         return this.selections.length;
16077     },
16078
16079     /**
16080      * Get the currently selected nodes.
16081      * @return {Array} An array of HTMLElements
16082      */
16083     getSelectedNodes : function(){
16084         return this.selections;
16085     },
16086
16087     /**
16088      * Get the indexes of the selected nodes.
16089      * @return {Array}
16090      */
16091     getSelectedIndexes : function(){
16092         var indexes = [], s = this.selections;
16093         for(var i = 0, len = s.length; i < len; i++){
16094             indexes.push(s[i].nodeIndex);
16095         }
16096         return indexes;
16097     },
16098
16099     /**
16100      * Clear all selections
16101      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16102      */
16103     clearSelections : function(suppressEvent){
16104         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16105             this.cmp.elements = this.selections;
16106             this.cmp.removeClass(this.selectedClass);
16107             this.selections = [];
16108             if(!suppressEvent){
16109                 this.fireEvent("selectionchange", this, this.selections);
16110             }
16111         }
16112     },
16113
16114     /**
16115      * Returns true if the passed node is selected
16116      * @param {HTMLElement/Number} node The node or node index
16117      * @return {Boolean}
16118      */
16119     isSelected : function(node){
16120         var s = this.selections;
16121         if(s.length < 1){
16122             return false;
16123         }
16124         node = this.getNode(node);
16125         return s.indexOf(node) !== -1;
16126     },
16127
16128     /**
16129      * Selects nodes.
16130      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16131      * @param {Boolean} keepExisting (optional) true to keep existing selections
16132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16133      */
16134     select : function(nodeInfo, keepExisting, suppressEvent){
16135         if(nodeInfo instanceof Array){
16136             if(!keepExisting){
16137                 this.clearSelections(true);
16138             }
16139             for(var i = 0, len = nodeInfo.length; i < len; i++){
16140                 this.select(nodeInfo[i], true, true);
16141             }
16142             return;
16143         } 
16144         var node = this.getNode(nodeInfo);
16145         if(!node || this.isSelected(node)){
16146             return; // already selected.
16147         }
16148         if(!keepExisting){
16149             this.clearSelections(true);
16150         }
16151         
16152         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16153             Roo.fly(node).addClass(this.selectedClass);
16154             this.selections.push(node);
16155             if(!suppressEvent){
16156                 this.fireEvent("selectionchange", this, this.selections);
16157             }
16158         }
16159         
16160         
16161     },
16162       /**
16163      * Unselects nodes.
16164      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16165      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16167      */
16168     unselect : function(nodeInfo, keepExisting, suppressEvent)
16169     {
16170         if(nodeInfo instanceof Array){
16171             Roo.each(this.selections, function(s) {
16172                 this.unselect(s, nodeInfo);
16173             }, this);
16174             return;
16175         }
16176         var node = this.getNode(nodeInfo);
16177         if(!node || !this.isSelected(node)){
16178             //Roo.log("not selected");
16179             return; // not selected.
16180         }
16181         // fireevent???
16182         var ns = [];
16183         Roo.each(this.selections, function(s) {
16184             if (s == node ) {
16185                 Roo.fly(node).removeClass(this.selectedClass);
16186
16187                 return;
16188             }
16189             ns.push(s);
16190         },this);
16191         
16192         this.selections= ns;
16193         this.fireEvent("selectionchange", this, this.selections);
16194     },
16195
16196     /**
16197      * Gets a template node.
16198      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16199      * @return {HTMLElement} The node or null if it wasn't found
16200      */
16201     getNode : function(nodeInfo){
16202         if(typeof nodeInfo == "string"){
16203             return document.getElementById(nodeInfo);
16204         }else if(typeof nodeInfo == "number"){
16205             return this.nodes[nodeInfo];
16206         }
16207         return nodeInfo;
16208     },
16209
16210     /**
16211      * Gets a range template nodes.
16212      * @param {Number} startIndex
16213      * @param {Number} endIndex
16214      * @return {Array} An array of nodes
16215      */
16216     getNodes : function(start, end){
16217         var ns = this.nodes;
16218         start = start || 0;
16219         end = typeof end == "undefined" ? ns.length - 1 : end;
16220         var nodes = [];
16221         if(start <= end){
16222             for(var i = start; i <= end; i++){
16223                 nodes.push(ns[i]);
16224             }
16225         } else{
16226             for(var i = start; i >= end; i--){
16227                 nodes.push(ns[i]);
16228             }
16229         }
16230         return nodes;
16231     },
16232
16233     /**
16234      * Finds the index of the passed node
16235      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16236      * @return {Number} The index of the node or -1
16237      */
16238     indexOf : function(node){
16239         node = this.getNode(node);
16240         if(typeof node.nodeIndex == "number"){
16241             return node.nodeIndex;
16242         }
16243         var ns = this.nodes;
16244         for(var i = 0, len = ns.length; i < len; i++){
16245             if(ns[i] == node){
16246                 return i;
16247             }
16248         }
16249         return -1;
16250     }
16251 });
16252 /*
16253  * - LGPL
16254  *
16255  * based on jquery fullcalendar
16256  * 
16257  */
16258
16259 Roo.bootstrap = Roo.bootstrap || {};
16260 /**
16261  * @class Roo.bootstrap.Calendar
16262  * @extends Roo.bootstrap.Component
16263  * Bootstrap Calendar class
16264  * @cfg {Boolean} loadMask (true|false) default false
16265  * @cfg {Object} header generate the user specific header of the calendar, default false
16266
16267  * @constructor
16268  * Create a new Container
16269  * @param {Object} config The config object
16270  */
16271
16272
16273
16274 Roo.bootstrap.Calendar = function(config){
16275     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16276      this.addEvents({
16277         /**
16278              * @event select
16279              * Fires when a date is selected
16280              * @param {DatePicker} this
16281              * @param {Date} date The selected date
16282              */
16283         'select': true,
16284         /**
16285              * @event monthchange
16286              * Fires when the displayed month changes 
16287              * @param {DatePicker} this
16288              * @param {Date} date The selected month
16289              */
16290         'monthchange': true,
16291         /**
16292              * @event evententer
16293              * Fires when mouse over an event
16294              * @param {Calendar} this
16295              * @param {event} Event
16296              */
16297         'evententer': true,
16298         /**
16299              * @event eventleave
16300              * Fires when the mouse leaves an
16301              * @param {Calendar} this
16302              * @param {event}
16303              */
16304         'eventleave': true,
16305         /**
16306              * @event eventclick
16307              * Fires when the mouse click an
16308              * @param {Calendar} this
16309              * @param {event}
16310              */
16311         'eventclick': true
16312         
16313     });
16314
16315 };
16316
16317 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16318     
16319      /**
16320      * @cfg {Number} startDay
16321      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16322      */
16323     startDay : 0,
16324     
16325     loadMask : false,
16326     
16327     header : false,
16328       
16329     getAutoCreate : function(){
16330         
16331         
16332         var fc_button = function(name, corner, style, content ) {
16333             return Roo.apply({},{
16334                 tag : 'span',
16335                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16336                          (corner.length ?
16337                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16338                             ''
16339                         ),
16340                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16341                 unselectable: 'on'
16342             });
16343         };
16344         
16345         var header = {};
16346         
16347         if(!this.header){
16348             header = {
16349                 tag : 'table',
16350                 cls : 'fc-header',
16351                 style : 'width:100%',
16352                 cn : [
16353                     {
16354                         tag: 'tr',
16355                         cn : [
16356                             {
16357                                 tag : 'td',
16358                                 cls : 'fc-header-left',
16359                                 cn : [
16360                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16361                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16362                                     { tag: 'span', cls: 'fc-header-space' },
16363                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16364
16365
16366                                 ]
16367                             },
16368
16369                             {
16370                                 tag : 'td',
16371                                 cls : 'fc-header-center',
16372                                 cn : [
16373                                     {
16374                                         tag: 'span',
16375                                         cls: 'fc-header-title',
16376                                         cn : {
16377                                             tag: 'H2',
16378                                             html : 'month / year'
16379                                         }
16380                                     }
16381
16382                                 ]
16383                             },
16384                             {
16385                                 tag : 'td',
16386                                 cls : 'fc-header-right',
16387                                 cn : [
16388                               /*      fc_button('month', 'left', '', 'month' ),
16389                                     fc_button('week', '', '', 'week' ),
16390                                     fc_button('day', 'right', '', 'day' )
16391                                 */    
16392
16393                                 ]
16394                             }
16395
16396                         ]
16397                     }
16398                 ]
16399             };
16400         }
16401         
16402         header = this.header;
16403         
16404        
16405         var cal_heads = function() {
16406             var ret = [];
16407             // fixme - handle this.
16408             
16409             for (var i =0; i < Date.dayNames.length; i++) {
16410                 var d = Date.dayNames[i];
16411                 ret.push({
16412                     tag: 'th',
16413                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16414                     html : d.substring(0,3)
16415                 });
16416                 
16417             }
16418             ret[0].cls += ' fc-first';
16419             ret[6].cls += ' fc-last';
16420             return ret;
16421         };
16422         var cal_cell = function(n) {
16423             return  {
16424                 tag: 'td',
16425                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16426                 cn : [
16427                     {
16428                         cn : [
16429                             {
16430                                 cls: 'fc-day-number',
16431                                 html: 'D'
16432                             },
16433                             {
16434                                 cls: 'fc-day-content',
16435                              
16436                                 cn : [
16437                                      {
16438                                         style: 'position: relative;' // height: 17px;
16439                                     }
16440                                 ]
16441                             }
16442                             
16443                             
16444                         ]
16445                     }
16446                 ]
16447                 
16448             }
16449         };
16450         var cal_rows = function() {
16451             
16452             var ret = [];
16453             for (var r = 0; r < 6; r++) {
16454                 var row= {
16455                     tag : 'tr',
16456                     cls : 'fc-week',
16457                     cn : []
16458                 };
16459                 
16460                 for (var i =0; i < Date.dayNames.length; i++) {
16461                     var d = Date.dayNames[i];
16462                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16463
16464                 }
16465                 row.cn[0].cls+=' fc-first';
16466                 row.cn[0].cn[0].style = 'min-height:90px';
16467                 row.cn[6].cls+=' fc-last';
16468                 ret.push(row);
16469                 
16470             }
16471             ret[0].cls += ' fc-first';
16472             ret[4].cls += ' fc-prev-last';
16473             ret[5].cls += ' fc-last';
16474             return ret;
16475             
16476         };
16477         
16478         var cal_table = {
16479             tag: 'table',
16480             cls: 'fc-border-separate',
16481             style : 'width:100%',
16482             cellspacing  : 0,
16483             cn : [
16484                 { 
16485                     tag: 'thead',
16486                     cn : [
16487                         { 
16488                             tag: 'tr',
16489                             cls : 'fc-first fc-last',
16490                             cn : cal_heads()
16491                         }
16492                     ]
16493                 },
16494                 { 
16495                     tag: 'tbody',
16496                     cn : cal_rows()
16497                 }
16498                   
16499             ]
16500         };
16501          
16502          var cfg = {
16503             cls : 'fc fc-ltr',
16504             cn : [
16505                 header,
16506                 {
16507                     cls : 'fc-content',
16508                     style : "position: relative;",
16509                     cn : [
16510                         {
16511                             cls : 'fc-view fc-view-month fc-grid',
16512                             style : 'position: relative',
16513                             unselectable : 'on',
16514                             cn : [
16515                                 {
16516                                     cls : 'fc-event-container',
16517                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16518                                 },
16519                                 cal_table
16520                             ]
16521                         }
16522                     ]
16523     
16524                 }
16525            ] 
16526             
16527         };
16528         
16529          
16530         
16531         return cfg;
16532     },
16533     
16534     
16535     initEvents : function()
16536     {
16537         if(!this.store){
16538             throw "can not find store for calendar";
16539         }
16540         
16541         var mark = {
16542             tag: "div",
16543             cls:"x-dlg-mask",
16544             style: "text-align:center",
16545             cn: [
16546                 {
16547                     tag: "div",
16548                     style: "background-color:white;width:50%;margin:250 auto",
16549                     cn: [
16550                         {
16551                             tag: "img",
16552                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16553                         },
16554                         {
16555                             tag: "span",
16556                             html: "Loading"
16557                         }
16558                         
16559                     ]
16560                 }
16561             ]
16562         };
16563         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16564         
16565         var size = this.el.select('.fc-content', true).first().getSize();
16566         this.maskEl.setSize(size.width, size.height);
16567         this.maskEl.enableDisplayMode("block");
16568         if(!this.loadMask){
16569             this.maskEl.hide();
16570         }
16571         
16572         this.store = Roo.factory(this.store, Roo.data);
16573         this.store.on('load', this.onLoad, this);
16574         this.store.on('beforeload', this.onBeforeLoad, this);
16575         
16576         this.resize();
16577         
16578         this.cells = this.el.select('.fc-day',true);
16579         //Roo.log(this.cells);
16580         this.textNodes = this.el.query('.fc-day-number');
16581         this.cells.addClassOnOver('fc-state-hover');
16582         
16583         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16584         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16585         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16586         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16587         
16588         this.on('monthchange', this.onMonthChange, this);
16589         
16590         this.update(new Date().clearTime());
16591     },
16592     
16593     resize : function() {
16594         var sz  = this.el.getSize();
16595         
16596         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16597         this.el.select('.fc-day-content div',true).setHeight(34);
16598     },
16599     
16600     
16601     // private
16602     showPrevMonth : function(e){
16603         this.update(this.activeDate.add("mo", -1));
16604     },
16605     showToday : function(e){
16606         this.update(new Date().clearTime());
16607     },
16608     // private
16609     showNextMonth : function(e){
16610         this.update(this.activeDate.add("mo", 1));
16611     },
16612
16613     // private
16614     showPrevYear : function(){
16615         this.update(this.activeDate.add("y", -1));
16616     },
16617
16618     // private
16619     showNextYear : function(){
16620         this.update(this.activeDate.add("y", 1));
16621     },
16622
16623     
16624    // private
16625     update : function(date)
16626     {
16627         var vd = this.activeDate;
16628         this.activeDate = date;
16629 //        if(vd && this.el){
16630 //            var t = date.getTime();
16631 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16632 //                Roo.log('using add remove');
16633 //                
16634 //                this.fireEvent('monthchange', this, date);
16635 //                
16636 //                this.cells.removeClass("fc-state-highlight");
16637 //                this.cells.each(function(c){
16638 //                   if(c.dateValue == t){
16639 //                       c.addClass("fc-state-highlight");
16640 //                       setTimeout(function(){
16641 //                            try{c.dom.firstChild.focus();}catch(e){}
16642 //                       }, 50);
16643 //                       return false;
16644 //                   }
16645 //                   return true;
16646 //                });
16647 //                return;
16648 //            }
16649 //        }
16650         
16651         var days = date.getDaysInMonth();
16652         
16653         var firstOfMonth = date.getFirstDateOfMonth();
16654         var startingPos = firstOfMonth.getDay()-this.startDay;
16655         
16656         if(startingPos < this.startDay){
16657             startingPos += 7;
16658         }
16659         
16660         var pm = date.add(Date.MONTH, -1);
16661         var prevStart = pm.getDaysInMonth()-startingPos;
16662 //        
16663         this.cells = this.el.select('.fc-day',true);
16664         this.textNodes = this.el.query('.fc-day-number');
16665         this.cells.addClassOnOver('fc-state-hover');
16666         
16667         var cells = this.cells.elements;
16668         var textEls = this.textNodes;
16669         
16670         Roo.each(cells, function(cell){
16671             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16672         });
16673         
16674         days += startingPos;
16675
16676         // convert everything to numbers so it's fast
16677         var day = 86400000;
16678         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16679         //Roo.log(d);
16680         //Roo.log(pm);
16681         //Roo.log(prevStart);
16682         
16683         var today = new Date().clearTime().getTime();
16684         var sel = date.clearTime().getTime();
16685         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16686         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16687         var ddMatch = this.disabledDatesRE;
16688         var ddText = this.disabledDatesText;
16689         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16690         var ddaysText = this.disabledDaysText;
16691         var format = this.format;
16692         
16693         var setCellClass = function(cal, cell){
16694             cell.row = 0;
16695             cell.events = [];
16696             cell.more = [];
16697             //Roo.log('set Cell Class');
16698             cell.title = "";
16699             var t = d.getTime();
16700             
16701             //Roo.log(d);
16702             
16703             cell.dateValue = t;
16704             if(t == today){
16705                 cell.className += " fc-today";
16706                 cell.className += " fc-state-highlight";
16707                 cell.title = cal.todayText;
16708             }
16709             if(t == sel){
16710                 // disable highlight in other month..
16711                 //cell.className += " fc-state-highlight";
16712                 
16713             }
16714             // disabling
16715             if(t < min) {
16716                 cell.className = " fc-state-disabled";
16717                 cell.title = cal.minText;
16718                 return;
16719             }
16720             if(t > max) {
16721                 cell.className = " fc-state-disabled";
16722                 cell.title = cal.maxText;
16723                 return;
16724             }
16725             if(ddays){
16726                 if(ddays.indexOf(d.getDay()) != -1){
16727                     cell.title = ddaysText;
16728                     cell.className = " fc-state-disabled";
16729                 }
16730             }
16731             if(ddMatch && format){
16732                 var fvalue = d.dateFormat(format);
16733                 if(ddMatch.test(fvalue)){
16734                     cell.title = ddText.replace("%0", fvalue);
16735                     cell.className = " fc-state-disabled";
16736                 }
16737             }
16738             
16739             if (!cell.initialClassName) {
16740                 cell.initialClassName = cell.dom.className;
16741             }
16742             
16743             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16744         };
16745
16746         var i = 0;
16747         
16748         for(; i < startingPos; i++) {
16749             textEls[i].innerHTML = (++prevStart);
16750             d.setDate(d.getDate()+1);
16751             
16752             cells[i].className = "fc-past fc-other-month";
16753             setCellClass(this, cells[i]);
16754         }
16755         
16756         var intDay = 0;
16757         
16758         for(; i < days; i++){
16759             intDay = i - startingPos + 1;
16760             textEls[i].innerHTML = (intDay);
16761             d.setDate(d.getDate()+1);
16762             
16763             cells[i].className = ''; // "x-date-active";
16764             setCellClass(this, cells[i]);
16765         }
16766         var extraDays = 0;
16767         
16768         for(; i < 42; i++) {
16769             textEls[i].innerHTML = (++extraDays);
16770             d.setDate(d.getDate()+1);
16771             
16772             cells[i].className = "fc-future fc-other-month";
16773             setCellClass(this, cells[i]);
16774         }
16775         
16776         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16777         
16778         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16779         
16780         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16781         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16782         
16783         if(totalRows != 6){
16784             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16785             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16786         }
16787         
16788         this.fireEvent('monthchange', this, date);
16789         
16790         
16791         /*
16792         if(!this.internalRender){
16793             var main = this.el.dom.firstChild;
16794             var w = main.offsetWidth;
16795             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16796             Roo.fly(main).setWidth(w);
16797             this.internalRender = true;
16798             // opera does not respect the auto grow header center column
16799             // then, after it gets a width opera refuses to recalculate
16800             // without a second pass
16801             if(Roo.isOpera && !this.secondPass){
16802                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16803                 this.secondPass = true;
16804                 this.update.defer(10, this, [date]);
16805             }
16806         }
16807         */
16808         
16809     },
16810     
16811     findCell : function(dt) {
16812         dt = dt.clearTime().getTime();
16813         var ret = false;
16814         this.cells.each(function(c){
16815             //Roo.log("check " +c.dateValue + '?=' + dt);
16816             if(c.dateValue == dt){
16817                 ret = c;
16818                 return false;
16819             }
16820             return true;
16821         });
16822         
16823         return ret;
16824     },
16825     
16826     findCells : function(ev) {
16827         var s = ev.start.clone().clearTime().getTime();
16828        // Roo.log(s);
16829         var e= ev.end.clone().clearTime().getTime();
16830        // Roo.log(e);
16831         var ret = [];
16832         this.cells.each(function(c){
16833              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16834             
16835             if(c.dateValue > e){
16836                 return ;
16837             }
16838             if(c.dateValue < s){
16839                 return ;
16840             }
16841             ret.push(c);
16842         });
16843         
16844         return ret;    
16845     },
16846     
16847 //    findBestRow: function(cells)
16848 //    {
16849 //        var ret = 0;
16850 //        
16851 //        for (var i =0 ; i < cells.length;i++) {
16852 //            ret  = Math.max(cells[i].rows || 0,ret);
16853 //        }
16854 //        return ret;
16855 //        
16856 //    },
16857     
16858     
16859     addItem : function(ev)
16860     {
16861         // look for vertical location slot in
16862         var cells = this.findCells(ev);
16863         
16864 //        ev.row = this.findBestRow(cells);
16865         
16866         // work out the location.
16867         
16868         var crow = false;
16869         var rows = [];
16870         for(var i =0; i < cells.length; i++) {
16871             
16872             cells[i].row = cells[0].row;
16873             
16874             if(i == 0){
16875                 cells[i].row = cells[i].row + 1;
16876             }
16877             
16878             if (!crow) {
16879                 crow = {
16880                     start : cells[i],
16881                     end :  cells[i]
16882                 };
16883                 continue;
16884             }
16885             if (crow.start.getY() == cells[i].getY()) {
16886                 // on same row.
16887                 crow.end = cells[i];
16888                 continue;
16889             }
16890             // different row.
16891             rows.push(crow);
16892             crow = {
16893                 start: cells[i],
16894                 end : cells[i]
16895             };
16896             
16897         }
16898         
16899         rows.push(crow);
16900         ev.els = [];
16901         ev.rows = rows;
16902         ev.cells = cells;
16903         
16904         cells[0].events.push(ev);
16905         
16906         this.calevents.push(ev);
16907     },
16908     
16909     clearEvents: function() {
16910         
16911         if(!this.calevents){
16912             return;
16913         }
16914         
16915         Roo.each(this.cells.elements, function(c){
16916             c.row = 0;
16917             c.events = [];
16918             c.more = [];
16919         });
16920         
16921         Roo.each(this.calevents, function(e) {
16922             Roo.each(e.els, function(el) {
16923                 el.un('mouseenter' ,this.onEventEnter, this);
16924                 el.un('mouseleave' ,this.onEventLeave, this);
16925                 el.remove();
16926             },this);
16927         },this);
16928         
16929         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16930             e.remove();
16931         });
16932         
16933     },
16934     
16935     renderEvents: function()
16936     {   
16937         var _this = this;
16938         
16939         this.cells.each(function(c) {
16940             
16941             if(c.row < 5){
16942                 return;
16943             }
16944             
16945             var ev = c.events;
16946             
16947             var r = 4;
16948             if(c.row != c.events.length){
16949                 r = 4 - (4 - (c.row - c.events.length));
16950             }
16951             
16952             c.events = ev.slice(0, r);
16953             c.more = ev.slice(r);
16954             
16955             if(c.more.length && c.more.length == 1){
16956                 c.events.push(c.more.pop());
16957             }
16958             
16959             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16960             
16961         });
16962             
16963         this.cells.each(function(c) {
16964             
16965             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16966             
16967             
16968             for (var e = 0; e < c.events.length; e++){
16969                 var ev = c.events[e];
16970                 var rows = ev.rows;
16971                 
16972                 for(var i = 0; i < rows.length; i++) {
16973                 
16974                     // how many rows should it span..
16975
16976                     var  cfg = {
16977                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16978                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16979
16980                         unselectable : "on",
16981                         cn : [
16982                             {
16983                                 cls: 'fc-event-inner',
16984                                 cn : [
16985     //                                {
16986     //                                  tag:'span',
16987     //                                  cls: 'fc-event-time',
16988     //                                  html : cells.length > 1 ? '' : ev.time
16989     //                                },
16990                                     {
16991                                       tag:'span',
16992                                       cls: 'fc-event-title',
16993                                       html : String.format('{0}', ev.title)
16994                                     }
16995
16996
16997                                 ]
16998                             },
16999                             {
17000                                 cls: 'ui-resizable-handle ui-resizable-e',
17001                                 html : '&nbsp;&nbsp;&nbsp'
17002                             }
17003
17004                         ]
17005                     };
17006
17007                     if (i == 0) {
17008                         cfg.cls += ' fc-event-start';
17009                     }
17010                     if ((i+1) == rows.length) {
17011                         cfg.cls += ' fc-event-end';
17012                     }
17013
17014                     var ctr = _this.el.select('.fc-event-container',true).first();
17015                     var cg = ctr.createChild(cfg);
17016
17017                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17018                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17019
17020                     var r = (c.more.length) ? 1 : 0;
17021                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17022                     cg.setWidth(ebox.right - sbox.x -2);
17023
17024                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17025                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17026                     cg.on('click', _this.onEventClick, _this, ev);
17027
17028                     ev.els.push(cg);
17029                     
17030                 }
17031                 
17032             }
17033             
17034             
17035             if(c.more.length){
17036                 var  cfg = {
17037                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17038                     style : 'position: absolute',
17039                     unselectable : "on",
17040                     cn : [
17041                         {
17042                             cls: 'fc-event-inner',
17043                             cn : [
17044                                 {
17045                                   tag:'span',
17046                                   cls: 'fc-event-title',
17047                                   html : 'More'
17048                                 }
17049
17050
17051                             ]
17052                         },
17053                         {
17054                             cls: 'ui-resizable-handle ui-resizable-e',
17055                             html : '&nbsp;&nbsp;&nbsp'
17056                         }
17057
17058                     ]
17059                 };
17060
17061                 var ctr = _this.el.select('.fc-event-container',true).first();
17062                 var cg = ctr.createChild(cfg);
17063
17064                 var sbox = c.select('.fc-day-content',true).first().getBox();
17065                 var ebox = c.select('.fc-day-content',true).first().getBox();
17066                 //Roo.log(cg);
17067                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17068                 cg.setWidth(ebox.right - sbox.x -2);
17069
17070                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17071                 
17072             }
17073             
17074         });
17075         
17076         
17077         
17078     },
17079     
17080     onEventEnter: function (e, el,event,d) {
17081         this.fireEvent('evententer', this, el, event);
17082     },
17083     
17084     onEventLeave: function (e, el,event,d) {
17085         this.fireEvent('eventleave', this, el, event);
17086     },
17087     
17088     onEventClick: function (e, el,event,d) {
17089         this.fireEvent('eventclick', this, el, event);
17090     },
17091     
17092     onMonthChange: function () {
17093         this.store.load();
17094     },
17095     
17096     onMoreEventClick: function(e, el, more)
17097     {
17098         var _this = this;
17099         
17100         this.calpopover.placement = 'right';
17101         this.calpopover.setTitle('More');
17102         
17103         this.calpopover.setContent('');
17104         
17105         var ctr = this.calpopover.el.select('.popover-content', true).first();
17106         
17107         Roo.each(more, function(m){
17108             var cfg = {
17109                 cls : 'fc-event-hori fc-event-draggable',
17110                 html : m.title
17111             };
17112             var cg = ctr.createChild(cfg);
17113             
17114             cg.on('click', _this.onEventClick, _this, m);
17115         });
17116         
17117         this.calpopover.show(el);
17118         
17119         
17120     },
17121     
17122     onLoad: function () 
17123     {   
17124         this.calevents = [];
17125         var cal = this;
17126         
17127         if(this.store.getCount() > 0){
17128             this.store.data.each(function(d){
17129                cal.addItem({
17130                     id : d.data.id,
17131                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17132                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17133                     time : d.data.start_time,
17134                     title : d.data.title,
17135                     description : d.data.description,
17136                     venue : d.data.venue
17137                 });
17138             });
17139         }
17140         
17141         this.renderEvents();
17142         
17143         if(this.calevents.length && this.loadMask){
17144             this.maskEl.hide();
17145         }
17146     },
17147     
17148     onBeforeLoad: function()
17149     {
17150         this.clearEvents();
17151         if(this.loadMask){
17152             this.maskEl.show();
17153         }
17154     }
17155 });
17156
17157  
17158  /*
17159  * - LGPL
17160  *
17161  * element
17162  * 
17163  */
17164
17165 /**
17166  * @class Roo.bootstrap.Popover
17167  * @extends Roo.bootstrap.Component
17168  * Bootstrap Popover class
17169  * @cfg {String} html contents of the popover   (or false to use children..)
17170  * @cfg {String} title of popover (or false to hide)
17171  * @cfg {String} placement how it is placed
17172  * @cfg {String} trigger click || hover (or false to trigger manually)
17173  * @cfg {String} over what (parent or false to trigger manually.)
17174  * @cfg {Number} delay - delay before showing
17175  
17176  * @constructor
17177  * Create a new Popover
17178  * @param {Object} config The config object
17179  */
17180
17181 Roo.bootstrap.Popover = function(config){
17182     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17183     
17184     this.addEvents({
17185         // raw events
17186          /**
17187          * @event show
17188          * After the popover show
17189          * 
17190          * @param {Roo.bootstrap.Popover} this
17191          */
17192         "show" : true,
17193         /**
17194          * @event hide
17195          * After the popover hide
17196          * 
17197          * @param {Roo.bootstrap.Popover} this
17198          */
17199         "hide" : true
17200     });
17201 };
17202
17203 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17204     
17205     title: 'Fill in a title',
17206     html: false,
17207     
17208     placement : 'right',
17209     trigger : 'hover', // hover
17210     
17211     delay : 0,
17212     
17213     over: 'parent',
17214     
17215     can_build_overlaid : false,
17216     
17217     getChildContainer : function()
17218     {
17219         return this.el.select('.popover-content',true).first();
17220     },
17221     
17222     getAutoCreate : function(){
17223          
17224         var cfg = {
17225            cls : 'popover roo-dynamic',
17226            style: 'display:block',
17227            cn : [
17228                 {
17229                     cls : 'arrow'
17230                 },
17231                 {
17232                     cls : 'popover-inner',
17233                     cn : [
17234                         {
17235                             tag: 'h3',
17236                             cls: 'popover-title',
17237                             html : this.title
17238                         },
17239                         {
17240                             cls : 'popover-content',
17241                             html : this.html
17242                         }
17243                     ]
17244                     
17245                 }
17246            ]
17247         };
17248         
17249         return cfg;
17250     },
17251     setTitle: function(str)
17252     {
17253         this.title = str;
17254         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17255     },
17256     setContent: function(str)
17257     {
17258         this.html = str;
17259         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17260     },
17261     // as it get's added to the bottom of the page.
17262     onRender : function(ct, position)
17263     {
17264         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17265         if(!this.el){
17266             var cfg = Roo.apply({},  this.getAutoCreate());
17267             cfg.id = Roo.id();
17268             
17269             if (this.cls) {
17270                 cfg.cls += ' ' + this.cls;
17271             }
17272             if (this.style) {
17273                 cfg.style = this.style;
17274             }
17275             //Roo.log("adding to ");
17276             this.el = Roo.get(document.body).createChild(cfg, position);
17277 //            Roo.log(this.el);
17278         }
17279         this.initEvents();
17280     },
17281     
17282     initEvents : function()
17283     {
17284         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17285         this.el.enableDisplayMode('block');
17286         this.el.hide();
17287         if (this.over === false) {
17288             return; 
17289         }
17290         if (this.triggers === false) {
17291             return;
17292         }
17293         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17294         var triggers = this.trigger ? this.trigger.split(' ') : [];
17295         Roo.each(triggers, function(trigger) {
17296         
17297             if (trigger == 'click') {
17298                 on_el.on('click', this.toggle, this);
17299             } else if (trigger != 'manual') {
17300                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17301                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17302       
17303                 on_el.on(eventIn  ,this.enter, this);
17304                 on_el.on(eventOut, this.leave, this);
17305             }
17306         }, this);
17307         
17308     },
17309     
17310     
17311     // private
17312     timeout : null,
17313     hoverState : null,
17314     
17315     toggle : function () {
17316         this.hoverState == 'in' ? this.leave() : this.enter();
17317     },
17318     
17319     enter : function () {
17320         
17321         clearTimeout(this.timeout);
17322     
17323         this.hoverState = 'in';
17324     
17325         if (!this.delay || !this.delay.show) {
17326             this.show();
17327             return;
17328         }
17329         var _t = this;
17330         this.timeout = setTimeout(function () {
17331             if (_t.hoverState == 'in') {
17332                 _t.show();
17333             }
17334         }, this.delay.show)
17335     },
17336     
17337     leave : function() {
17338         clearTimeout(this.timeout);
17339     
17340         this.hoverState = 'out';
17341     
17342         if (!this.delay || !this.delay.hide) {
17343             this.hide();
17344             return;
17345         }
17346         var _t = this;
17347         this.timeout = setTimeout(function () {
17348             if (_t.hoverState == 'out') {
17349                 _t.hide();
17350             }
17351         }, this.delay.hide)
17352     },
17353     
17354     show : function (on_el)
17355     {
17356         if (!on_el) {
17357             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17358         }
17359         
17360         // set content.
17361         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17362         if (this.html !== false) {
17363             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17364         }
17365         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17366         if (!this.title.length) {
17367             this.el.select('.popover-title',true).hide();
17368         }
17369         
17370         var placement = typeof this.placement == 'function' ?
17371             this.placement.call(this, this.el, on_el) :
17372             this.placement;
17373             
17374         var autoToken = /\s?auto?\s?/i;
17375         var autoPlace = autoToken.test(placement);
17376         if (autoPlace) {
17377             placement = placement.replace(autoToken, '') || 'top';
17378         }
17379         
17380         //this.el.detach()
17381         //this.el.setXY([0,0]);
17382         this.el.show();
17383         this.el.dom.style.display='block';
17384         this.el.addClass(placement);
17385         
17386         //this.el.appendTo(on_el);
17387         
17388         var p = this.getPosition();
17389         var box = this.el.getBox();
17390         
17391         if (autoPlace) {
17392             // fixme..
17393         }
17394         var align = Roo.bootstrap.Popover.alignment[placement];
17395         
17396 //        Roo.log(align);
17397         this.el.alignTo(on_el, align[0],align[1]);
17398         //var arrow = this.el.select('.arrow',true).first();
17399         //arrow.set(align[2], 
17400         
17401         this.el.addClass('in');
17402         
17403         
17404         if (this.el.hasClass('fade')) {
17405             // fade it?
17406         }
17407         
17408         this.hoverState = 'in';
17409         
17410         this.fireEvent('show', this);
17411         
17412     },
17413     hide : function()
17414     {
17415         this.el.setXY([0,0]);
17416         this.el.removeClass('in');
17417         this.el.hide();
17418         this.hoverState = null;
17419         
17420         this.fireEvent('hide', this);
17421     }
17422     
17423 });
17424
17425 Roo.bootstrap.Popover.alignment = {
17426     'left' : ['r-l', [-10,0], 'right'],
17427     'right' : ['l-r', [10,0], 'left'],
17428     'bottom' : ['t-b', [0,10], 'top'],
17429     'top' : [ 'b-t', [0,-10], 'bottom']
17430 };
17431
17432  /*
17433  * - LGPL
17434  *
17435  * Progress
17436  * 
17437  */
17438
17439 /**
17440  * @class Roo.bootstrap.Progress
17441  * @extends Roo.bootstrap.Component
17442  * Bootstrap Progress class
17443  * @cfg {Boolean} striped striped of the progress bar
17444  * @cfg {Boolean} active animated of the progress bar
17445  * 
17446  * 
17447  * @constructor
17448  * Create a new Progress
17449  * @param {Object} config The config object
17450  */
17451
17452 Roo.bootstrap.Progress = function(config){
17453     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17454 };
17455
17456 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17457     
17458     striped : false,
17459     active: false,
17460     
17461     getAutoCreate : function(){
17462         var cfg = {
17463             tag: 'div',
17464             cls: 'progress'
17465         };
17466         
17467         
17468         if(this.striped){
17469             cfg.cls += ' progress-striped';
17470         }
17471       
17472         if(this.active){
17473             cfg.cls += ' active';
17474         }
17475         
17476         
17477         return cfg;
17478     }
17479    
17480 });
17481
17482  
17483
17484  /*
17485  * - LGPL
17486  *
17487  * ProgressBar
17488  * 
17489  */
17490
17491 /**
17492  * @class Roo.bootstrap.ProgressBar
17493  * @extends Roo.bootstrap.Component
17494  * Bootstrap ProgressBar class
17495  * @cfg {Number} aria_valuenow aria-value now
17496  * @cfg {Number} aria_valuemin aria-value min
17497  * @cfg {Number} aria_valuemax aria-value max
17498  * @cfg {String} label label for the progress bar
17499  * @cfg {String} panel (success | info | warning | danger )
17500  * @cfg {String} role role of the progress bar
17501  * @cfg {String} sr_only text
17502  * 
17503  * 
17504  * @constructor
17505  * Create a new ProgressBar
17506  * @param {Object} config The config object
17507  */
17508
17509 Roo.bootstrap.ProgressBar = function(config){
17510     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17511 };
17512
17513 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17514     
17515     aria_valuenow : 0,
17516     aria_valuemin : 0,
17517     aria_valuemax : 100,
17518     label : false,
17519     panel : false,
17520     role : false,
17521     sr_only: false,
17522     
17523     getAutoCreate : function()
17524     {
17525         
17526         var cfg = {
17527             tag: 'div',
17528             cls: 'progress-bar',
17529             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17530         };
17531         
17532         if(this.sr_only){
17533             cfg.cn = {
17534                 tag: 'span',
17535                 cls: 'sr-only',
17536                 html: this.sr_only
17537             }
17538         }
17539         
17540         if(this.role){
17541             cfg.role = this.role;
17542         }
17543         
17544         if(this.aria_valuenow){
17545             cfg['aria-valuenow'] = this.aria_valuenow;
17546         }
17547         
17548         if(this.aria_valuemin){
17549             cfg['aria-valuemin'] = this.aria_valuemin;
17550         }
17551         
17552         if(this.aria_valuemax){
17553             cfg['aria-valuemax'] = this.aria_valuemax;
17554         }
17555         
17556         if(this.label && !this.sr_only){
17557             cfg.html = this.label;
17558         }
17559         
17560         if(this.panel){
17561             cfg.cls += ' progress-bar-' + this.panel;
17562         }
17563         
17564         return cfg;
17565     },
17566     
17567     update : function(aria_valuenow)
17568     {
17569         this.aria_valuenow = aria_valuenow;
17570         
17571         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17572     }
17573    
17574 });
17575
17576  
17577
17578  /*
17579  * - LGPL
17580  *
17581  * column
17582  * 
17583  */
17584
17585 /**
17586  * @class Roo.bootstrap.TabGroup
17587  * @extends Roo.bootstrap.Column
17588  * Bootstrap Column class
17589  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17590  * @cfg {Boolean} carousel true to make the group behave like a carousel
17591  * @cfg {Boolean} bullets show bullets for the panels
17592  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17593  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17594  * @cfg {Boolean} showarrow (true|false) show arrow default true
17595  * 
17596  * @constructor
17597  * Create a new TabGroup
17598  * @param {Object} config The config object
17599  */
17600
17601 Roo.bootstrap.TabGroup = function(config){
17602     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17603     if (!this.navId) {
17604         this.navId = Roo.id();
17605     }
17606     this.tabs = [];
17607     Roo.bootstrap.TabGroup.register(this);
17608     
17609 };
17610
17611 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17612     
17613     carousel : false,
17614     transition : false,
17615     bullets : 0,
17616     timer : 0,
17617     autoslide : false,
17618     slideFn : false,
17619     slideOnTouch : false,
17620     showarrow : true,
17621     
17622     getAutoCreate : function()
17623     {
17624         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17625         
17626         cfg.cls += ' tab-content';
17627         
17628         if (this.carousel) {
17629             cfg.cls += ' carousel slide';
17630             
17631             cfg.cn = [{
17632                cls : 'carousel-inner',
17633                cn : []
17634             }];
17635         
17636             if(this.bullets  && !Roo.isTouch){
17637                 
17638                 var bullets = {
17639                     cls : 'carousel-bullets',
17640                     cn : []
17641                 };
17642                
17643                 if(this.bullets_cls){
17644                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17645                 }
17646                 
17647                 bullets.cn.push({
17648                     cls : 'clear'
17649                 });
17650                 
17651                 cfg.cn[0].cn.push(bullets);
17652             }
17653             
17654             if(this.showarrow){
17655                 cfg.cn[0].cn.push({
17656                     tag : 'div',
17657                     class : 'carousel-arrow',
17658                     cn : [
17659                         {
17660                             tag : 'div',
17661                             class : 'carousel-prev',
17662                             cn : [
17663                                 {
17664                                     tag : 'i',
17665                                     class : 'fa fa-chevron-left'
17666                                 }
17667                             ]
17668                         },
17669                         {
17670                             tag : 'div',
17671                             class : 'carousel-next',
17672                             cn : [
17673                                 {
17674                                     tag : 'i',
17675                                     class : 'fa fa-chevron-right'
17676                                 }
17677                             ]
17678                         }
17679                     ]
17680                 });
17681             }
17682             
17683         }
17684         
17685         return cfg;
17686     },
17687     
17688     initEvents:  function()
17689     {
17690 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17691 //            this.el.on("touchstart", this.onTouchStart, this);
17692 //        }
17693         
17694         if(this.autoslide){
17695             var _this = this;
17696             
17697             this.slideFn = window.setInterval(function() {
17698                 _this.showPanelNext();
17699             }, this.timer);
17700         }
17701         
17702         if(this.showarrow){
17703             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17704             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17705         }
17706         
17707         
17708     },
17709     
17710 //    onTouchStart : function(e, el, o)
17711 //    {
17712 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17713 //            return;
17714 //        }
17715 //        
17716 //        this.showPanelNext();
17717 //    },
17718     
17719     
17720     getChildContainer : function()
17721     {
17722         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17723     },
17724     
17725     /**
17726     * register a Navigation item
17727     * @param {Roo.bootstrap.NavItem} the navitem to add
17728     */
17729     register : function(item)
17730     {
17731         this.tabs.push( item);
17732         item.navId = this.navId; // not really needed..
17733         this.addBullet();
17734     
17735     },
17736     
17737     getActivePanel : function()
17738     {
17739         var r = false;
17740         Roo.each(this.tabs, function(t) {
17741             if (t.active) {
17742                 r = t;
17743                 return false;
17744             }
17745             return null;
17746         });
17747         return r;
17748         
17749     },
17750     getPanelByName : function(n)
17751     {
17752         var r = false;
17753         Roo.each(this.tabs, function(t) {
17754             if (t.tabId == n) {
17755                 r = t;
17756                 return false;
17757             }
17758             return null;
17759         });
17760         return r;
17761     },
17762     indexOfPanel : function(p)
17763     {
17764         var r = false;
17765         Roo.each(this.tabs, function(t,i) {
17766             if (t.tabId == p.tabId) {
17767                 r = i;
17768                 return false;
17769             }
17770             return null;
17771         });
17772         return r;
17773     },
17774     /**
17775      * show a specific panel
17776      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17777      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17778      */
17779     showPanel : function (pan)
17780     {
17781         if(this.transition || typeof(pan) == 'undefined'){
17782             Roo.log("waiting for the transitionend");
17783             return;
17784         }
17785         
17786         if (typeof(pan) == 'number') {
17787             pan = this.tabs[pan];
17788         }
17789         
17790         if (typeof(pan) == 'string') {
17791             pan = this.getPanelByName(pan);
17792         }
17793         
17794         var cur = this.getActivePanel();
17795         
17796         if(!pan || !cur){
17797             Roo.log('pan or acitve pan is undefined');
17798             return false;
17799         }
17800         
17801         if (pan.tabId == this.getActivePanel().tabId) {
17802             return true;
17803         }
17804         
17805         if (false === cur.fireEvent('beforedeactivate')) {
17806             return false;
17807         }
17808         
17809         if(this.bullets > 0 && !Roo.isTouch){
17810             this.setActiveBullet(this.indexOfPanel(pan));
17811         }
17812         
17813         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17814             
17815             this.transition = true;
17816             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17817             var lr = dir == 'next' ? 'left' : 'right';
17818             pan.el.addClass(dir); // or prev
17819             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17820             cur.el.addClass(lr); // or right
17821             pan.el.addClass(lr);
17822             
17823             var _this = this;
17824             cur.el.on('transitionend', function() {
17825                 Roo.log("trans end?");
17826                 
17827                 pan.el.removeClass([lr,dir]);
17828                 pan.setActive(true);
17829                 
17830                 cur.el.removeClass([lr]);
17831                 cur.setActive(false);
17832                 
17833                 _this.transition = false;
17834                 
17835             }, this, { single:  true } );
17836             
17837             return true;
17838         }
17839         
17840         cur.setActive(false);
17841         pan.setActive(true);
17842         
17843         return true;
17844         
17845     },
17846     showPanelNext : function()
17847     {
17848         var i = this.indexOfPanel(this.getActivePanel());
17849         
17850         if (i >= this.tabs.length - 1 && !this.autoslide) {
17851             return;
17852         }
17853         
17854         if (i >= this.tabs.length - 1 && this.autoslide) {
17855             i = -1;
17856         }
17857         
17858         this.showPanel(this.tabs[i+1]);
17859     },
17860     
17861     showPanelPrev : function()
17862     {
17863         var i = this.indexOfPanel(this.getActivePanel());
17864         
17865         if (i  < 1 && !this.autoslide) {
17866             return;
17867         }
17868         
17869         if (i < 1 && this.autoslide) {
17870             i = this.tabs.length;
17871         }
17872         
17873         this.showPanel(this.tabs[i-1]);
17874     },
17875     
17876     
17877     addBullet: function()
17878     {
17879         if(!this.bullets || Roo.isTouch){
17880             return;
17881         }
17882         var ctr = this.el.select('.carousel-bullets',true).first();
17883         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17884         var bullet = ctr.createChild({
17885             cls : 'bullet bullet-' + i
17886         },ctr.dom.lastChild);
17887         
17888         
17889         var _this = this;
17890         
17891         bullet.on('click', (function(e, el, o, ii, t){
17892
17893             e.preventDefault();
17894
17895             this.showPanel(ii);
17896
17897             if(this.autoslide && this.slideFn){
17898                 clearInterval(this.slideFn);
17899                 this.slideFn = window.setInterval(function() {
17900                     _this.showPanelNext();
17901                 }, this.timer);
17902             }
17903
17904         }).createDelegate(this, [i, bullet], true));
17905                 
17906         
17907     },
17908      
17909     setActiveBullet : function(i)
17910     {
17911         if(Roo.isTouch){
17912             return;
17913         }
17914         
17915         Roo.each(this.el.select('.bullet', true).elements, function(el){
17916             el.removeClass('selected');
17917         });
17918
17919         var bullet = this.el.select('.bullet-' + i, true).first();
17920         
17921         if(!bullet){
17922             return;
17923         }
17924         
17925         bullet.addClass('selected');
17926     }
17927     
17928     
17929   
17930 });
17931
17932  
17933
17934  
17935  
17936 Roo.apply(Roo.bootstrap.TabGroup, {
17937     
17938     groups: {},
17939      /**
17940     * register a Navigation Group
17941     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17942     */
17943     register : function(navgrp)
17944     {
17945         this.groups[navgrp.navId] = navgrp;
17946         
17947     },
17948     /**
17949     * fetch a Navigation Group based on the navigation ID
17950     * if one does not exist , it will get created.
17951     * @param {string} the navgroup to add
17952     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17953     */
17954     get: function(navId) {
17955         if (typeof(this.groups[navId]) == 'undefined') {
17956             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17957         }
17958         return this.groups[navId] ;
17959     }
17960     
17961     
17962     
17963 });
17964
17965  /*
17966  * - LGPL
17967  *
17968  * TabPanel
17969  * 
17970  */
17971
17972 /**
17973  * @class Roo.bootstrap.TabPanel
17974  * @extends Roo.bootstrap.Component
17975  * Bootstrap TabPanel class
17976  * @cfg {Boolean} active panel active
17977  * @cfg {String} html panel content
17978  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17979  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17980  * @cfg {String} href click to link..
17981  * 
17982  * 
17983  * @constructor
17984  * Create a new TabPanel
17985  * @param {Object} config The config object
17986  */
17987
17988 Roo.bootstrap.TabPanel = function(config){
17989     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17990     this.addEvents({
17991         /**
17992              * @event changed
17993              * Fires when the active status changes
17994              * @param {Roo.bootstrap.TabPanel} this
17995              * @param {Boolean} state the new state
17996             
17997          */
17998         'changed': true,
17999         /**
18000              * @event beforedeactivate
18001              * Fires before a tab is de-activated - can be used to do validation on a form.
18002              * @param {Roo.bootstrap.TabPanel} this
18003              * @return {Boolean} false if there is an error
18004             
18005          */
18006         'beforedeactivate': true
18007      });
18008     
18009     this.tabId = this.tabId || Roo.id();
18010   
18011 };
18012
18013 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18014     
18015     active: false,
18016     html: false,
18017     tabId: false,
18018     navId : false,
18019     href : '',
18020     
18021     getAutoCreate : function(){
18022         var cfg = {
18023             tag: 'div',
18024             // item is needed for carousel - not sure if it has any effect otherwise
18025             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18026             html: this.html || ''
18027         };
18028         
18029         if(this.active){
18030             cfg.cls += ' active';
18031         }
18032         
18033         if(this.tabId){
18034             cfg.tabId = this.tabId;
18035         }
18036         
18037         
18038         return cfg;
18039     },
18040     
18041     initEvents:  function()
18042     {
18043         var p = this.parent();
18044         
18045         this.navId = this.navId || p.navId;
18046         
18047         if (typeof(this.navId) != 'undefined') {
18048             // not really needed.. but just in case.. parent should be a NavGroup.
18049             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18050             
18051             tg.register(this);
18052             
18053             var i = tg.tabs.length - 1;
18054             
18055             if(this.active && tg.bullets > 0 && i < tg.bullets){
18056                 tg.setActiveBullet(i);
18057             }
18058         }
18059         
18060         this.el.on('click', this.onClick, this);
18061         
18062         if(Roo.isTouch){
18063             this.el.on("touchstart", this.onTouchStart, this);
18064             this.el.on("touchmove", this.onTouchMove, this);
18065             this.el.on("touchend", this.onTouchEnd, this);
18066         }
18067         
18068     },
18069     
18070     onRender : function(ct, position)
18071     {
18072         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18073     },
18074     
18075     setActive : function(state)
18076     {
18077         Roo.log("panel - set active " + this.tabId + "=" + state);
18078         
18079         this.active = state;
18080         if (!state) {
18081             this.el.removeClass('active');
18082             
18083         } else  if (!this.el.hasClass('active')) {
18084             this.el.addClass('active');
18085         }
18086         
18087         this.fireEvent('changed', this, state);
18088     },
18089     
18090     onClick : function(e)
18091     {
18092         e.preventDefault();
18093         
18094         if(!this.href.length){
18095             return;
18096         }
18097         
18098         window.location.href = this.href;
18099     },
18100     
18101     startX : 0,
18102     startY : 0,
18103     endX : 0,
18104     endY : 0,
18105     swiping : false,
18106     
18107     onTouchStart : function(e)
18108     {
18109         this.swiping = false;
18110         
18111         this.startX = e.browserEvent.touches[0].clientX;
18112         this.startY = e.browserEvent.touches[0].clientY;
18113     },
18114     
18115     onTouchMove : function(e)
18116     {
18117         this.swiping = true;
18118         
18119         this.endX = e.browserEvent.touches[0].clientX;
18120         this.endY = e.browserEvent.touches[0].clientY;
18121     },
18122     
18123     onTouchEnd : function(e)
18124     {
18125         if(!this.swiping){
18126             this.onClick(e);
18127             return;
18128         }
18129         
18130         var tabGroup = this.parent();
18131         
18132         if(this.endX > this.startX){ // swiping right
18133             tabGroup.showPanelPrev();
18134             return;
18135         }
18136         
18137         if(this.startX > this.endX){ // swiping left
18138             tabGroup.showPanelNext();
18139             return;
18140         }
18141     }
18142     
18143     
18144 });
18145  
18146
18147  
18148
18149  /*
18150  * - LGPL
18151  *
18152  * DateField
18153  * 
18154  */
18155
18156 /**
18157  * @class Roo.bootstrap.DateField
18158  * @extends Roo.bootstrap.Input
18159  * Bootstrap DateField class
18160  * @cfg {Number} weekStart default 0
18161  * @cfg {String} viewMode default empty, (months|years)
18162  * @cfg {String} minViewMode default empty, (months|years)
18163  * @cfg {Number} startDate default -Infinity
18164  * @cfg {Number} endDate default Infinity
18165  * @cfg {Boolean} todayHighlight default false
18166  * @cfg {Boolean} todayBtn default false
18167  * @cfg {Boolean} calendarWeeks default false
18168  * @cfg {Object} daysOfWeekDisabled default empty
18169  * @cfg {Boolean} singleMode default false (true | false)
18170  * 
18171  * @cfg {Boolean} keyboardNavigation default true
18172  * @cfg {String} language default en
18173  * 
18174  * @constructor
18175  * Create a new DateField
18176  * @param {Object} config The config object
18177  */
18178
18179 Roo.bootstrap.DateField = function(config){
18180     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18181      this.addEvents({
18182             /**
18183              * @event show
18184              * Fires when this field show.
18185              * @param {Roo.bootstrap.DateField} this
18186              * @param {Mixed} date The date value
18187              */
18188             show : true,
18189             /**
18190              * @event show
18191              * Fires when this field hide.
18192              * @param {Roo.bootstrap.DateField} this
18193              * @param {Mixed} date The date value
18194              */
18195             hide : true,
18196             /**
18197              * @event select
18198              * Fires when select a date.
18199              * @param {Roo.bootstrap.DateField} this
18200              * @param {Mixed} date The date value
18201              */
18202             select : true,
18203             /**
18204              * @event beforeselect
18205              * Fires when before select a date.
18206              * @param {Roo.bootstrap.DateField} this
18207              * @param {Mixed} date The date value
18208              */
18209             beforeselect : true
18210         });
18211 };
18212
18213 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18214     
18215     /**
18216      * @cfg {String} format
18217      * The default date format string which can be overriden for localization support.  The format must be
18218      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18219      */
18220     format : "m/d/y",
18221     /**
18222      * @cfg {String} altFormats
18223      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18224      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18225      */
18226     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18227     
18228     weekStart : 0,
18229     
18230     viewMode : '',
18231     
18232     minViewMode : '',
18233     
18234     todayHighlight : false,
18235     
18236     todayBtn: false,
18237     
18238     language: 'en',
18239     
18240     keyboardNavigation: true,
18241     
18242     calendarWeeks: false,
18243     
18244     startDate: -Infinity,
18245     
18246     endDate: Infinity,
18247     
18248     daysOfWeekDisabled: [],
18249     
18250     _events: [],
18251     
18252     singleMode : false,
18253     
18254     UTCDate: function()
18255     {
18256         return new Date(Date.UTC.apply(Date, arguments));
18257     },
18258     
18259     UTCToday: function()
18260     {
18261         var today = new Date();
18262         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18263     },
18264     
18265     getDate: function() {
18266             var d = this.getUTCDate();
18267             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18268     },
18269     
18270     getUTCDate: function() {
18271             return this.date;
18272     },
18273     
18274     setDate: function(d) {
18275             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18276     },
18277     
18278     setUTCDate: function(d) {
18279             this.date = d;
18280             this.setValue(this.formatDate(this.date));
18281     },
18282         
18283     onRender: function(ct, position)
18284     {
18285         
18286         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18287         
18288         this.language = this.language || 'en';
18289         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18290         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18291         
18292         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18293         this.format = this.format || 'm/d/y';
18294         this.isInline = false;
18295         this.isInput = true;
18296         this.component = this.el.select('.add-on', true).first() || false;
18297         this.component = (this.component && this.component.length === 0) ? false : this.component;
18298         this.hasInput = this.component && this.inputEl().length;
18299         
18300         if (typeof(this.minViewMode === 'string')) {
18301             switch (this.minViewMode) {
18302                 case 'months':
18303                     this.minViewMode = 1;
18304                     break;
18305                 case 'years':
18306                     this.minViewMode = 2;
18307                     break;
18308                 default:
18309                     this.minViewMode = 0;
18310                     break;
18311             }
18312         }
18313         
18314         if (typeof(this.viewMode === 'string')) {
18315             switch (this.viewMode) {
18316                 case 'months':
18317                     this.viewMode = 1;
18318                     break;
18319                 case 'years':
18320                     this.viewMode = 2;
18321                     break;
18322                 default:
18323                     this.viewMode = 0;
18324                     break;
18325             }
18326         }
18327                 
18328         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18329         
18330 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18331         
18332         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18333         
18334         this.picker().on('mousedown', this.onMousedown, this);
18335         this.picker().on('click', this.onClick, this);
18336         
18337         this.picker().addClass('datepicker-dropdown');
18338         
18339         this.startViewMode = this.viewMode;
18340         
18341         if(this.singleMode){
18342             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18343                 v.setVisibilityMode(Roo.Element.DISPLAY);
18344                 v.hide();
18345             });
18346             
18347             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18348                 v.setStyle('width', '189px');
18349             });
18350         }
18351         
18352         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18353             if(!this.calendarWeeks){
18354                 v.remove();
18355                 return;
18356             }
18357             
18358             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18359             v.attr('colspan', function(i, val){
18360                 return parseInt(val) + 1;
18361             });
18362         });
18363                         
18364         
18365         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18366         
18367         this.setStartDate(this.startDate);
18368         this.setEndDate(this.endDate);
18369         
18370         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18371         
18372         this.fillDow();
18373         this.fillMonths();
18374         this.update();
18375         this.showMode();
18376         
18377         if(this.isInline) {
18378             this.show();
18379         }
18380     },
18381     
18382     picker : function()
18383     {
18384         return this.pickerEl;
18385 //        return this.el.select('.datepicker', true).first();
18386     },
18387     
18388     fillDow: function()
18389     {
18390         var dowCnt = this.weekStart;
18391         
18392         var dow = {
18393             tag: 'tr',
18394             cn: [
18395                 
18396             ]
18397         };
18398         
18399         if(this.calendarWeeks){
18400             dow.cn.push({
18401                 tag: 'th',
18402                 cls: 'cw',
18403                 html: '&nbsp;'
18404             })
18405         }
18406         
18407         while (dowCnt < this.weekStart + 7) {
18408             dow.cn.push({
18409                 tag: 'th',
18410                 cls: 'dow',
18411                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18412             });
18413         }
18414         
18415         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18416     },
18417     
18418     fillMonths: function()
18419     {    
18420         var i = 0;
18421         var months = this.picker().select('>.datepicker-months td', true).first();
18422         
18423         months.dom.innerHTML = '';
18424         
18425         while (i < 12) {
18426             var month = {
18427                 tag: 'span',
18428                 cls: 'month',
18429                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18430             };
18431             
18432             months.createChild(month);
18433         }
18434         
18435     },
18436     
18437     update: function()
18438     {
18439         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
18440         
18441         if (this.date < this.startDate) {
18442             this.viewDate = new Date(this.startDate);
18443         } else if (this.date > this.endDate) {
18444             this.viewDate = new Date(this.endDate);
18445         } else {
18446             this.viewDate = new Date(this.date);
18447         }
18448         
18449         this.fill();
18450     },
18451     
18452     fill: function() 
18453     {
18454         var d = new Date(this.viewDate),
18455                 year = d.getUTCFullYear(),
18456                 month = d.getUTCMonth(),
18457                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18458                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18459                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18460                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18461                 currentDate = this.date && this.date.valueOf(),
18462                 today = this.UTCToday();
18463         
18464         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18465         
18466 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18467         
18468 //        this.picker.select('>tfoot th.today').
18469 //                                              .text(dates[this.language].today)
18470 //                                              .toggle(this.todayBtn !== false);
18471     
18472         this.updateNavArrows();
18473         this.fillMonths();
18474                                                 
18475         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18476         
18477         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18478          
18479         prevMonth.setUTCDate(day);
18480         
18481         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18482         
18483         var nextMonth = new Date(prevMonth);
18484         
18485         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18486         
18487         nextMonth = nextMonth.valueOf();
18488         
18489         var fillMonths = false;
18490         
18491         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18492         
18493         while(prevMonth.valueOf() < nextMonth) {
18494             var clsName = '';
18495             
18496             if (prevMonth.getUTCDay() === this.weekStart) {
18497                 if(fillMonths){
18498                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18499                 }
18500                     
18501                 fillMonths = {
18502                     tag: 'tr',
18503                     cn: []
18504                 };
18505                 
18506                 if(this.calendarWeeks){
18507                     // ISO 8601: First week contains first thursday.
18508                     // ISO also states week starts on Monday, but we can be more abstract here.
18509                     var
18510                     // Start of current week: based on weekstart/current date
18511                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18512                     // Thursday of this week
18513                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18514                     // First Thursday of year, year from thursday
18515                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18516                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18517                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18518                     
18519                     fillMonths.cn.push({
18520                         tag: 'td',
18521                         cls: 'cw',
18522                         html: calWeek
18523                     });
18524                 }
18525             }
18526             
18527             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18528                 clsName += ' old';
18529             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18530                 clsName += ' new';
18531             }
18532             if (this.todayHighlight &&
18533                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18534                 prevMonth.getUTCMonth() == today.getMonth() &&
18535                 prevMonth.getUTCDate() == today.getDate()) {
18536                 clsName += ' today';
18537             }
18538             
18539             if (currentDate && prevMonth.valueOf() === currentDate) {
18540                 clsName += ' active';
18541             }
18542             
18543             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18544                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18545                     clsName += ' disabled';
18546             }
18547             
18548             fillMonths.cn.push({
18549                 tag: 'td',
18550                 cls: 'day ' + clsName,
18551                 html: prevMonth.getDate()
18552             });
18553             
18554             prevMonth.setDate(prevMonth.getDate()+1);
18555         }
18556           
18557         var currentYear = this.date && this.date.getUTCFullYear();
18558         var currentMonth = this.date && this.date.getUTCMonth();
18559         
18560         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18561         
18562         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18563             v.removeClass('active');
18564             
18565             if(currentYear === year && k === currentMonth){
18566                 v.addClass('active');
18567             }
18568             
18569             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18570                 v.addClass('disabled');
18571             }
18572             
18573         });
18574         
18575         
18576         year = parseInt(year/10, 10) * 10;
18577         
18578         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18579         
18580         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18581         
18582         year -= 1;
18583         for (var i = -1; i < 11; i++) {
18584             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18585                 tag: 'span',
18586                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18587                 html: year
18588             });
18589             
18590             year += 1;
18591         }
18592     },
18593     
18594     showMode: function(dir) 
18595     {
18596         if (dir) {
18597             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18598         }
18599         
18600         Roo.each(this.picker().select('>div',true).elements, function(v){
18601             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18602             v.hide();
18603         });
18604         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18605     },
18606     
18607     place: function()
18608     {
18609         if(this.isInline) {
18610             return;
18611         }
18612         
18613         this.picker().removeClass(['bottom', 'top']);
18614         
18615         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18616             /*
18617              * place to the top of element!
18618              *
18619              */
18620             
18621             this.picker().addClass('top');
18622             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18623             
18624             return;
18625         }
18626         
18627         this.picker().addClass('bottom');
18628         
18629         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18630     },
18631     
18632     parseDate : function(value)
18633     {
18634         if(!value || value instanceof Date){
18635             return value;
18636         }
18637         var v = Date.parseDate(value, this.format);
18638         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18639             v = Date.parseDate(value, 'Y-m-d');
18640         }
18641         if(!v && this.altFormats){
18642             if(!this.altFormatsArray){
18643                 this.altFormatsArray = this.altFormats.split("|");
18644             }
18645             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18646                 v = Date.parseDate(value, this.altFormatsArray[i]);
18647             }
18648         }
18649         return v;
18650     },
18651     
18652     formatDate : function(date, fmt)
18653     {   
18654         return (!date || !(date instanceof Date)) ?
18655         date : date.dateFormat(fmt || this.format);
18656     },
18657     
18658     onFocus : function()
18659     {
18660         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18661         this.show();
18662     },
18663     
18664     onBlur : function()
18665     {
18666         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18667         
18668         var d = this.inputEl().getValue();
18669         
18670         this.setValue(d);
18671                 
18672         this.hide();
18673     },
18674     
18675     show : function()
18676     {
18677         this.picker().show();
18678         this.update();
18679         this.place();
18680         
18681         this.fireEvent('show', this, this.date);
18682     },
18683     
18684     hide : function()
18685     {
18686         if(this.isInline) {
18687             return;
18688         }
18689         this.picker().hide();
18690         this.viewMode = this.startViewMode;
18691         this.showMode();
18692         
18693         this.fireEvent('hide', this, this.date);
18694         
18695     },
18696     
18697     onMousedown: function(e)
18698     {
18699         e.stopPropagation();
18700         e.preventDefault();
18701     },
18702     
18703     keyup: function(e)
18704     {
18705         Roo.bootstrap.DateField.superclass.keyup.call(this);
18706         this.update();
18707     },
18708
18709     setValue: function(v)
18710     {
18711         if(this.fireEvent('beforeselect', this, v) !== false){
18712             var d = new Date(this.parseDate(v) ).clearTime();
18713         
18714             if(isNaN(d.getTime())){
18715                 this.date = this.viewDate = '';
18716                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18717                 return;
18718             }
18719
18720             v = this.formatDate(d);
18721
18722             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18723
18724             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18725
18726             this.update();
18727
18728             this.fireEvent('select', this, this.date);
18729         }
18730     },
18731     
18732     getValue: function()
18733     {
18734         return this.formatDate(this.date);
18735     },
18736     
18737     fireKey: function(e)
18738     {
18739         if (!this.picker().isVisible()){
18740             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18741                 this.show();
18742             }
18743             return;
18744         }
18745         
18746         var dateChanged = false,
18747         dir, day, month,
18748         newDate, newViewDate;
18749         
18750         switch(e.keyCode){
18751             case 27: // escape
18752                 this.hide();
18753                 e.preventDefault();
18754                 break;
18755             case 37: // left
18756             case 39: // right
18757                 if (!this.keyboardNavigation) {
18758                     break;
18759                 }
18760                 dir = e.keyCode == 37 ? -1 : 1;
18761                 
18762                 if (e.ctrlKey){
18763                     newDate = this.moveYear(this.date, dir);
18764                     newViewDate = this.moveYear(this.viewDate, dir);
18765                 } else if (e.shiftKey){
18766                     newDate = this.moveMonth(this.date, dir);
18767                     newViewDate = this.moveMonth(this.viewDate, dir);
18768                 } else {
18769                     newDate = new Date(this.date);
18770                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18771                     newViewDate = new Date(this.viewDate);
18772                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18773                 }
18774                 if (this.dateWithinRange(newDate)){
18775                     this.date = newDate;
18776                     this.viewDate = newViewDate;
18777                     this.setValue(this.formatDate(this.date));
18778 //                    this.update();
18779                     e.preventDefault();
18780                     dateChanged = true;
18781                 }
18782                 break;
18783             case 38: // up
18784             case 40: // down
18785                 if (!this.keyboardNavigation) {
18786                     break;
18787                 }
18788                 dir = e.keyCode == 38 ? -1 : 1;
18789                 if (e.ctrlKey){
18790                     newDate = this.moveYear(this.date, dir);
18791                     newViewDate = this.moveYear(this.viewDate, dir);
18792                 } else if (e.shiftKey){
18793                     newDate = this.moveMonth(this.date, dir);
18794                     newViewDate = this.moveMonth(this.viewDate, dir);
18795                 } else {
18796                     newDate = new Date(this.date);
18797                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18798                     newViewDate = new Date(this.viewDate);
18799                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18800                 }
18801                 if (this.dateWithinRange(newDate)){
18802                     this.date = newDate;
18803                     this.viewDate = newViewDate;
18804                     this.setValue(this.formatDate(this.date));
18805 //                    this.update();
18806                     e.preventDefault();
18807                     dateChanged = true;
18808                 }
18809                 break;
18810             case 13: // enter
18811                 this.setValue(this.formatDate(this.date));
18812                 this.hide();
18813                 e.preventDefault();
18814                 break;
18815             case 9: // tab
18816                 this.setValue(this.formatDate(this.date));
18817                 this.hide();
18818                 break;
18819             case 16: // shift
18820             case 17: // ctrl
18821             case 18: // alt
18822                 break;
18823             default :
18824                 this.hide();
18825                 
18826         }
18827     },
18828     
18829     
18830     onClick: function(e) 
18831     {
18832         e.stopPropagation();
18833         e.preventDefault();
18834         
18835         var target = e.getTarget();
18836         
18837         if(target.nodeName.toLowerCase() === 'i'){
18838             target = Roo.get(target).dom.parentNode;
18839         }
18840         
18841         var nodeName = target.nodeName;
18842         var className = target.className;
18843         var html = target.innerHTML;
18844         //Roo.log(nodeName);
18845         
18846         switch(nodeName.toLowerCase()) {
18847             case 'th':
18848                 switch(className) {
18849                     case 'switch':
18850                         this.showMode(1);
18851                         break;
18852                     case 'prev':
18853                     case 'next':
18854                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18855                         switch(this.viewMode){
18856                                 case 0:
18857                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18858                                         break;
18859                                 case 1:
18860                                 case 2:
18861                                         this.viewDate = this.moveYear(this.viewDate, dir);
18862                                         break;
18863                         }
18864                         this.fill();
18865                         break;
18866                     case 'today':
18867                         var date = new Date();
18868                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18869 //                        this.fill()
18870                         this.setValue(this.formatDate(this.date));
18871                         
18872                         this.hide();
18873                         break;
18874                 }
18875                 break;
18876             case 'span':
18877                 if (className.indexOf('disabled') < 0) {
18878                     this.viewDate.setUTCDate(1);
18879                     if (className.indexOf('month') > -1) {
18880                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18881                     } else {
18882                         var year = parseInt(html, 10) || 0;
18883                         this.viewDate.setUTCFullYear(year);
18884                         
18885                     }
18886                     
18887                     if(this.singleMode){
18888                         this.setValue(this.formatDate(this.viewDate));
18889                         this.hide();
18890                         return;
18891                     }
18892                     
18893                     this.showMode(-1);
18894                     this.fill();
18895                 }
18896                 break;
18897                 
18898             case 'td':
18899                 //Roo.log(className);
18900                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18901                     var day = parseInt(html, 10) || 1;
18902                     var year = this.viewDate.getUTCFullYear(),
18903                         month = this.viewDate.getUTCMonth();
18904
18905                     if (className.indexOf('old') > -1) {
18906                         if(month === 0 ){
18907                             month = 11;
18908                             year -= 1;
18909                         }else{
18910                             month -= 1;
18911                         }
18912                     } else if (className.indexOf('new') > -1) {
18913                         if (month == 11) {
18914                             month = 0;
18915                             year += 1;
18916                         } else {
18917                             month += 1;
18918                         }
18919                     }
18920                     //Roo.log([year,month,day]);
18921                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18922                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18923 //                    this.fill();
18924                     //Roo.log(this.formatDate(this.date));
18925                     this.setValue(this.formatDate(this.date));
18926                     this.hide();
18927                 }
18928                 break;
18929         }
18930     },
18931     
18932     setStartDate: function(startDate)
18933     {
18934         this.startDate = startDate || -Infinity;
18935         if (this.startDate !== -Infinity) {
18936             this.startDate = this.parseDate(this.startDate);
18937         }
18938         this.update();
18939         this.updateNavArrows();
18940     },
18941
18942     setEndDate: function(endDate)
18943     {
18944         this.endDate = endDate || Infinity;
18945         if (this.endDate !== Infinity) {
18946             this.endDate = this.parseDate(this.endDate);
18947         }
18948         this.update();
18949         this.updateNavArrows();
18950     },
18951     
18952     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18953     {
18954         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18955         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18956             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18957         }
18958         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18959             return parseInt(d, 10);
18960         });
18961         this.update();
18962         this.updateNavArrows();
18963     },
18964     
18965     updateNavArrows: function() 
18966     {
18967         if(this.singleMode){
18968             return;
18969         }
18970         
18971         var d = new Date(this.viewDate),
18972         year = d.getUTCFullYear(),
18973         month = d.getUTCMonth();
18974         
18975         Roo.each(this.picker().select('.prev', true).elements, function(v){
18976             v.show();
18977             switch (this.viewMode) {
18978                 case 0:
18979
18980                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18981                         v.hide();
18982                     }
18983                     break;
18984                 case 1:
18985                 case 2:
18986                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18987                         v.hide();
18988                     }
18989                     break;
18990             }
18991         });
18992         
18993         Roo.each(this.picker().select('.next', true).elements, function(v){
18994             v.show();
18995             switch (this.viewMode) {
18996                 case 0:
18997
18998                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18999                         v.hide();
19000                     }
19001                     break;
19002                 case 1:
19003                 case 2:
19004                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19005                         v.hide();
19006                     }
19007                     break;
19008             }
19009         })
19010     },
19011     
19012     moveMonth: function(date, dir)
19013     {
19014         if (!dir) {
19015             return date;
19016         }
19017         var new_date = new Date(date.valueOf()),
19018         day = new_date.getUTCDate(),
19019         month = new_date.getUTCMonth(),
19020         mag = Math.abs(dir),
19021         new_month, test;
19022         dir = dir > 0 ? 1 : -1;
19023         if (mag == 1){
19024             test = dir == -1
19025             // If going back one month, make sure month is not current month
19026             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19027             ? function(){
19028                 return new_date.getUTCMonth() == month;
19029             }
19030             // If going forward one month, make sure month is as expected
19031             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19032             : function(){
19033                 return new_date.getUTCMonth() != new_month;
19034             };
19035             new_month = month + dir;
19036             new_date.setUTCMonth(new_month);
19037             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19038             if (new_month < 0 || new_month > 11) {
19039                 new_month = (new_month + 12) % 12;
19040             }
19041         } else {
19042             // For magnitudes >1, move one month at a time...
19043             for (var i=0; i<mag; i++) {
19044                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19045                 new_date = this.moveMonth(new_date, dir);
19046             }
19047             // ...then reset the day, keeping it in the new month
19048             new_month = new_date.getUTCMonth();
19049             new_date.setUTCDate(day);
19050             test = function(){
19051                 return new_month != new_date.getUTCMonth();
19052             };
19053         }
19054         // Common date-resetting loop -- if date is beyond end of month, make it
19055         // end of month
19056         while (test()){
19057             new_date.setUTCDate(--day);
19058             new_date.setUTCMonth(new_month);
19059         }
19060         return new_date;
19061     },
19062
19063     moveYear: function(date, dir)
19064     {
19065         return this.moveMonth(date, dir*12);
19066     },
19067
19068     dateWithinRange: function(date)
19069     {
19070         return date >= this.startDate && date <= this.endDate;
19071     },
19072
19073     
19074     remove: function() 
19075     {
19076         this.picker().remove();
19077     },
19078     
19079     validateValue : function(value)
19080     {
19081         if(value.length < 1)  {
19082             if(this.allowBlank){
19083                 return true;
19084             }
19085             return false;
19086         }
19087         
19088         if(value.length < this.minLength){
19089             return false;
19090         }
19091         if(value.length > this.maxLength){
19092             return false;
19093         }
19094         if(this.vtype){
19095             var vt = Roo.form.VTypes;
19096             if(!vt[this.vtype](value, this)){
19097                 return false;
19098             }
19099         }
19100         if(typeof this.validator == "function"){
19101             var msg = this.validator(value);
19102             if(msg !== true){
19103                 return false;
19104             }
19105         }
19106         
19107         if(this.regex && !this.regex.test(value)){
19108             return false;
19109         }
19110         
19111         if(typeof(this.parseDate(value)) == 'undefined'){
19112             return false;
19113         }
19114         
19115         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19116             return false;
19117         }      
19118         
19119         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19120             return false;
19121         } 
19122         
19123         
19124         return true;
19125     }
19126    
19127 });
19128
19129 Roo.apply(Roo.bootstrap.DateField,  {
19130     
19131     head : {
19132         tag: 'thead',
19133         cn: [
19134         {
19135             tag: 'tr',
19136             cn: [
19137             {
19138                 tag: 'th',
19139                 cls: 'prev',
19140                 html: '<i class="fa fa-arrow-left"/>'
19141             },
19142             {
19143                 tag: 'th',
19144                 cls: 'switch',
19145                 colspan: '5'
19146             },
19147             {
19148                 tag: 'th',
19149                 cls: 'next',
19150                 html: '<i class="fa fa-arrow-right"/>'
19151             }
19152
19153             ]
19154         }
19155         ]
19156     },
19157     
19158     content : {
19159         tag: 'tbody',
19160         cn: [
19161         {
19162             tag: 'tr',
19163             cn: [
19164             {
19165                 tag: 'td',
19166                 colspan: '7'
19167             }
19168             ]
19169         }
19170         ]
19171     },
19172     
19173     footer : {
19174         tag: 'tfoot',
19175         cn: [
19176         {
19177             tag: 'tr',
19178             cn: [
19179             {
19180                 tag: 'th',
19181                 colspan: '7',
19182                 cls: 'today'
19183             }
19184                     
19185             ]
19186         }
19187         ]
19188     },
19189     
19190     dates:{
19191         en: {
19192             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19193             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19194             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19195             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19196             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19197             today: "Today"
19198         }
19199     },
19200     
19201     modes: [
19202     {
19203         clsName: 'days',
19204         navFnc: 'Month',
19205         navStep: 1
19206     },
19207     {
19208         clsName: 'months',
19209         navFnc: 'FullYear',
19210         navStep: 1
19211     },
19212     {
19213         clsName: 'years',
19214         navFnc: 'FullYear',
19215         navStep: 10
19216     }]
19217 });
19218
19219 Roo.apply(Roo.bootstrap.DateField,  {
19220   
19221     template : {
19222         tag: 'div',
19223         cls: 'datepicker dropdown-menu roo-dynamic',
19224         cn: [
19225         {
19226             tag: 'div',
19227             cls: 'datepicker-days',
19228             cn: [
19229             {
19230                 tag: 'table',
19231                 cls: 'table-condensed',
19232                 cn:[
19233                 Roo.bootstrap.DateField.head,
19234                 {
19235                     tag: 'tbody'
19236                 },
19237                 Roo.bootstrap.DateField.footer
19238                 ]
19239             }
19240             ]
19241         },
19242         {
19243             tag: 'div',
19244             cls: 'datepicker-months',
19245             cn: [
19246             {
19247                 tag: 'table',
19248                 cls: 'table-condensed',
19249                 cn:[
19250                 Roo.bootstrap.DateField.head,
19251                 Roo.bootstrap.DateField.content,
19252                 Roo.bootstrap.DateField.footer
19253                 ]
19254             }
19255             ]
19256         },
19257         {
19258             tag: 'div',
19259             cls: 'datepicker-years',
19260             cn: [
19261             {
19262                 tag: 'table',
19263                 cls: 'table-condensed',
19264                 cn:[
19265                 Roo.bootstrap.DateField.head,
19266                 Roo.bootstrap.DateField.content,
19267                 Roo.bootstrap.DateField.footer
19268                 ]
19269             }
19270             ]
19271         }
19272         ]
19273     }
19274 });
19275
19276  
19277
19278  /*
19279  * - LGPL
19280  *
19281  * TimeField
19282  * 
19283  */
19284
19285 /**
19286  * @class Roo.bootstrap.TimeField
19287  * @extends Roo.bootstrap.Input
19288  * Bootstrap DateField class
19289  * 
19290  * 
19291  * @constructor
19292  * Create a new TimeField
19293  * @param {Object} config The config object
19294  */
19295
19296 Roo.bootstrap.TimeField = function(config){
19297     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19298     this.addEvents({
19299             /**
19300              * @event show
19301              * Fires when this field show.
19302              * @param {Roo.bootstrap.DateField} thisthis
19303              * @param {Mixed} date The date value
19304              */
19305             show : true,
19306             /**
19307              * @event show
19308              * Fires when this field hide.
19309              * @param {Roo.bootstrap.DateField} this
19310              * @param {Mixed} date The date value
19311              */
19312             hide : true,
19313             /**
19314              * @event select
19315              * Fires when select a date.
19316              * @param {Roo.bootstrap.DateField} this
19317              * @param {Mixed} date The date value
19318              */
19319             select : true
19320         });
19321 };
19322
19323 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19324     
19325     /**
19326      * @cfg {String} format
19327      * The default time format string which can be overriden for localization support.  The format must be
19328      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19329      */
19330     format : "H:i",
19331        
19332     onRender: function(ct, position)
19333     {
19334         
19335         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19336                 
19337         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19338         
19339         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340         
19341         this.pop = this.picker().select('>.datepicker-time',true).first();
19342         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19343         
19344         this.picker().on('mousedown', this.onMousedown, this);
19345         this.picker().on('click', this.onClick, this);
19346         
19347         this.picker().addClass('datepicker-dropdown');
19348     
19349         this.fillTime();
19350         this.update();
19351             
19352         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19353         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19354         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19355         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19356         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19357         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19358
19359     },
19360     
19361     fireKey: function(e){
19362         if (!this.picker().isVisible()){
19363             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19364                 this.show();
19365             }
19366             return;
19367         }
19368
19369         e.preventDefault();
19370         
19371         switch(e.keyCode){
19372             case 27: // escape
19373                 this.hide();
19374                 break;
19375             case 37: // left
19376             case 39: // right
19377                 this.onTogglePeriod();
19378                 break;
19379             case 38: // up
19380                 this.onIncrementMinutes();
19381                 break;
19382             case 40: // down
19383                 this.onDecrementMinutes();
19384                 break;
19385             case 13: // enter
19386             case 9: // tab
19387                 this.setTime();
19388                 break;
19389         }
19390     },
19391     
19392     onClick: function(e) {
19393         e.stopPropagation();
19394         e.preventDefault();
19395     },
19396     
19397     picker : function()
19398     {
19399         return this.el.select('.datepicker', true).first();
19400     },
19401     
19402     fillTime: function()
19403     {    
19404         var time = this.pop.select('tbody', true).first();
19405         
19406         time.dom.innerHTML = '';
19407         
19408         time.createChild({
19409             tag: 'tr',
19410             cn: [
19411                 {
19412                     tag: 'td',
19413                     cn: [
19414                         {
19415                             tag: 'a',
19416                             href: '#',
19417                             cls: 'btn',
19418                             cn: [
19419                                 {
19420                                     tag: 'span',
19421                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19422                                 }
19423                             ]
19424                         } 
19425                     ]
19426                 },
19427                 {
19428                     tag: 'td',
19429                     cls: 'separator'
19430                 },
19431                 {
19432                     tag: 'td',
19433                     cn: [
19434                         {
19435                             tag: 'a',
19436                             href: '#',
19437                             cls: 'btn',
19438                             cn: [
19439                                 {
19440                                     tag: 'span',
19441                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19442                                 }
19443                             ]
19444                         }
19445                     ]
19446                 },
19447                 {
19448                     tag: 'td',
19449                     cls: 'separator'
19450                 }
19451             ]
19452         });
19453         
19454         time.createChild({
19455             tag: 'tr',
19456             cn: [
19457                 {
19458                     tag: 'td',
19459                     cn: [
19460                         {
19461                             tag: 'span',
19462                             cls: 'timepicker-hour',
19463                             html: '00'
19464                         }  
19465                     ]
19466                 },
19467                 {
19468                     tag: 'td',
19469                     cls: 'separator',
19470                     html: ':'
19471                 },
19472                 {
19473                     tag: 'td',
19474                     cn: [
19475                         {
19476                             tag: 'span',
19477                             cls: 'timepicker-minute',
19478                             html: '00'
19479                         }  
19480                     ]
19481                 },
19482                 {
19483                     tag: 'td',
19484                     cls: 'separator'
19485                 },
19486                 {
19487                     tag: 'td',
19488                     cn: [
19489                         {
19490                             tag: 'button',
19491                             type: 'button',
19492                             cls: 'btn btn-primary period',
19493                             html: 'AM'
19494                             
19495                         }
19496                     ]
19497                 }
19498             ]
19499         });
19500         
19501         time.createChild({
19502             tag: 'tr',
19503             cn: [
19504                 {
19505                     tag: 'td',
19506                     cn: [
19507                         {
19508                             tag: 'a',
19509                             href: '#',
19510                             cls: 'btn',
19511                             cn: [
19512                                 {
19513                                     tag: 'span',
19514                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19515                                 }
19516                             ]
19517                         }
19518                     ]
19519                 },
19520                 {
19521                     tag: 'td',
19522                     cls: 'separator'
19523                 },
19524                 {
19525                     tag: 'td',
19526                     cn: [
19527                         {
19528                             tag: 'a',
19529                             href: '#',
19530                             cls: 'btn',
19531                             cn: [
19532                                 {
19533                                     tag: 'span',
19534                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19535                                 }
19536                             ]
19537                         }
19538                     ]
19539                 },
19540                 {
19541                     tag: 'td',
19542                     cls: 'separator'
19543                 }
19544             ]
19545         });
19546         
19547     },
19548     
19549     update: function()
19550     {
19551         
19552         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19553         
19554         this.fill();
19555     },
19556     
19557     fill: function() 
19558     {
19559         var hours = this.time.getHours();
19560         var minutes = this.time.getMinutes();
19561         var period = 'AM';
19562         
19563         if(hours > 11){
19564             period = 'PM';
19565         }
19566         
19567         if(hours == 0){
19568             hours = 12;
19569         }
19570         
19571         
19572         if(hours > 12){
19573             hours = hours - 12;
19574         }
19575         
19576         if(hours < 10){
19577             hours = '0' + hours;
19578         }
19579         
19580         if(minutes < 10){
19581             minutes = '0' + minutes;
19582         }
19583         
19584         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19585         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19586         this.pop.select('button', true).first().dom.innerHTML = period;
19587         
19588     },
19589     
19590     place: function()
19591     {   
19592         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19593         
19594         var cls = ['bottom'];
19595         
19596         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19597             cls.pop();
19598             cls.push('top');
19599         }
19600         
19601         cls.push('right');
19602         
19603         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19604             cls.pop();
19605             cls.push('left');
19606         }
19607         
19608         this.picker().addClass(cls.join('-'));
19609         
19610         var _this = this;
19611         
19612         Roo.each(cls, function(c){
19613             if(c == 'bottom'){
19614                 _this.picker().setTop(_this.inputEl().getHeight());
19615                 return;
19616             }
19617             if(c == 'top'){
19618                 _this.picker().setTop(0 - _this.picker().getHeight());
19619                 return;
19620             }
19621             
19622             if(c == 'left'){
19623                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19624                 return;
19625             }
19626             if(c == 'right'){
19627                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19628                 return;
19629             }
19630         });
19631         
19632     },
19633   
19634     onFocus : function()
19635     {
19636         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19637         this.show();
19638     },
19639     
19640     onBlur : function()
19641     {
19642         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19643         this.hide();
19644     },
19645     
19646     show : function()
19647     {
19648         this.picker().show();
19649         this.pop.show();
19650         this.update();
19651         this.place();
19652         
19653         this.fireEvent('show', this, this.date);
19654     },
19655     
19656     hide : function()
19657     {
19658         this.picker().hide();
19659         this.pop.hide();
19660         
19661         this.fireEvent('hide', this, this.date);
19662     },
19663     
19664     setTime : function()
19665     {
19666         this.hide();
19667         this.setValue(this.time.format(this.format));
19668         
19669         this.fireEvent('select', this, this.date);
19670         
19671         
19672     },
19673     
19674     onMousedown: function(e){
19675         e.stopPropagation();
19676         e.preventDefault();
19677     },
19678     
19679     onIncrementHours: function()
19680     {
19681         Roo.log('onIncrementHours');
19682         this.time = this.time.add(Date.HOUR, 1);
19683         this.update();
19684         
19685     },
19686     
19687     onDecrementHours: function()
19688     {
19689         Roo.log('onDecrementHours');
19690         this.time = this.time.add(Date.HOUR, -1);
19691         this.update();
19692     },
19693     
19694     onIncrementMinutes: function()
19695     {
19696         Roo.log('onIncrementMinutes');
19697         this.time = this.time.add(Date.MINUTE, 1);
19698         this.update();
19699     },
19700     
19701     onDecrementMinutes: function()
19702     {
19703         Roo.log('onDecrementMinutes');
19704         this.time = this.time.add(Date.MINUTE, -1);
19705         this.update();
19706     },
19707     
19708     onTogglePeriod: function()
19709     {
19710         Roo.log('onTogglePeriod');
19711         this.time = this.time.add(Date.HOUR, 12);
19712         this.update();
19713     }
19714     
19715    
19716 });
19717
19718 Roo.apply(Roo.bootstrap.TimeField,  {
19719     
19720     content : {
19721         tag: 'tbody',
19722         cn: [
19723             {
19724                 tag: 'tr',
19725                 cn: [
19726                 {
19727                     tag: 'td',
19728                     colspan: '7'
19729                 }
19730                 ]
19731             }
19732         ]
19733     },
19734     
19735     footer : {
19736         tag: 'tfoot',
19737         cn: [
19738             {
19739                 tag: 'tr',
19740                 cn: [
19741                 {
19742                     tag: 'th',
19743                     colspan: '7',
19744                     cls: '',
19745                     cn: [
19746                         {
19747                             tag: 'button',
19748                             cls: 'btn btn-info ok',
19749                             html: 'OK'
19750                         }
19751                     ]
19752                 }
19753
19754                 ]
19755             }
19756         ]
19757     }
19758 });
19759
19760 Roo.apply(Roo.bootstrap.TimeField,  {
19761   
19762     template : {
19763         tag: 'div',
19764         cls: 'datepicker dropdown-menu',
19765         cn: [
19766             {
19767                 tag: 'div',
19768                 cls: 'datepicker-time',
19769                 cn: [
19770                 {
19771                     tag: 'table',
19772                     cls: 'table-condensed',
19773                     cn:[
19774                     Roo.bootstrap.TimeField.content,
19775                     Roo.bootstrap.TimeField.footer
19776                     ]
19777                 }
19778                 ]
19779             }
19780         ]
19781     }
19782 });
19783
19784  
19785
19786  /*
19787  * - LGPL
19788  *
19789  * MonthField
19790  * 
19791  */
19792
19793 /**
19794  * @class Roo.bootstrap.MonthField
19795  * @extends Roo.bootstrap.Input
19796  * Bootstrap MonthField class
19797  * 
19798  * @cfg {String} language default en
19799  * 
19800  * @constructor
19801  * Create a new MonthField
19802  * @param {Object} config The config object
19803  */
19804
19805 Roo.bootstrap.MonthField = function(config){
19806     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19807     
19808     this.addEvents({
19809         /**
19810          * @event show
19811          * Fires when this field show.
19812          * @param {Roo.bootstrap.MonthField} this
19813          * @param {Mixed} date The date value
19814          */
19815         show : true,
19816         /**
19817          * @event show
19818          * Fires when this field hide.
19819          * @param {Roo.bootstrap.MonthField} this
19820          * @param {Mixed} date The date value
19821          */
19822         hide : true,
19823         /**
19824          * @event select
19825          * Fires when select a date.
19826          * @param {Roo.bootstrap.MonthField} this
19827          * @param {String} oldvalue The old value
19828          * @param {String} newvalue The new value
19829          */
19830         select : true
19831     });
19832 };
19833
19834 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19835     
19836     onRender: function(ct, position)
19837     {
19838         
19839         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19840         
19841         this.language = this.language || 'en';
19842         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19843         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19844         
19845         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19846         this.isInline = false;
19847         this.isInput = true;
19848         this.component = this.el.select('.add-on', true).first() || false;
19849         this.component = (this.component && this.component.length === 0) ? false : this.component;
19850         this.hasInput = this.component && this.inputEL().length;
19851         
19852         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19853         
19854         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19855         
19856         this.picker().on('mousedown', this.onMousedown, this);
19857         this.picker().on('click', this.onClick, this);
19858         
19859         this.picker().addClass('datepicker-dropdown');
19860         
19861         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19862             v.setStyle('width', '189px');
19863         });
19864         
19865         this.fillMonths();
19866         
19867         this.update();
19868         
19869         if(this.isInline) {
19870             this.show();
19871         }
19872         
19873     },
19874     
19875     setValue: function(v, suppressEvent)
19876     {   
19877         var o = this.getValue();
19878         
19879         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19880         
19881         this.update();
19882
19883         if(suppressEvent !== true){
19884             this.fireEvent('select', this, o, v);
19885         }
19886         
19887     },
19888     
19889     getValue: function()
19890     {
19891         return this.value;
19892     },
19893     
19894     onClick: function(e) 
19895     {
19896         e.stopPropagation();
19897         e.preventDefault();
19898         
19899         var target = e.getTarget();
19900         
19901         if(target.nodeName.toLowerCase() === 'i'){
19902             target = Roo.get(target).dom.parentNode;
19903         }
19904         
19905         var nodeName = target.nodeName;
19906         var className = target.className;
19907         var html = target.innerHTML;
19908         
19909         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19910             return;
19911         }
19912         
19913         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19914         
19915         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19916         
19917         this.hide();
19918                         
19919     },
19920     
19921     picker : function()
19922     {
19923         return this.pickerEl;
19924     },
19925     
19926     fillMonths: function()
19927     {    
19928         var i = 0;
19929         var months = this.picker().select('>.datepicker-months td', true).first();
19930         
19931         months.dom.innerHTML = '';
19932         
19933         while (i < 12) {
19934             var month = {
19935                 tag: 'span',
19936                 cls: 'month',
19937                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19938             };
19939             
19940             months.createChild(month);
19941         }
19942         
19943     },
19944     
19945     update: function()
19946     {
19947         var _this = this;
19948         
19949         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19950             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19951         }
19952         
19953         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19954             e.removeClass('active');
19955             
19956             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19957                 e.addClass('active');
19958             }
19959         })
19960     },
19961     
19962     place: function()
19963     {
19964         if(this.isInline) {
19965             return;
19966         }
19967         
19968         this.picker().removeClass(['bottom', 'top']);
19969         
19970         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19971             /*
19972              * place to the top of element!
19973              *
19974              */
19975             
19976             this.picker().addClass('top');
19977             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19978             
19979             return;
19980         }
19981         
19982         this.picker().addClass('bottom');
19983         
19984         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19985     },
19986     
19987     onFocus : function()
19988     {
19989         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19990         this.show();
19991     },
19992     
19993     onBlur : function()
19994     {
19995         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19996         
19997         var d = this.inputEl().getValue();
19998         
19999         this.setValue(d);
20000                 
20001         this.hide();
20002     },
20003     
20004     show : function()
20005     {
20006         this.picker().show();
20007         this.picker().select('>.datepicker-months', true).first().show();
20008         this.update();
20009         this.place();
20010         
20011         this.fireEvent('show', this, this.date);
20012     },
20013     
20014     hide : function()
20015     {
20016         if(this.isInline) {
20017             return;
20018         }
20019         this.picker().hide();
20020         this.fireEvent('hide', this, this.date);
20021         
20022     },
20023     
20024     onMousedown: function(e)
20025     {
20026         e.stopPropagation();
20027         e.preventDefault();
20028     },
20029     
20030     keyup: function(e)
20031     {
20032         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20033         this.update();
20034     },
20035
20036     fireKey: function(e)
20037     {
20038         if (!this.picker().isVisible()){
20039             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20040                 this.show();
20041             }
20042             return;
20043         }
20044         
20045         var dir;
20046         
20047         switch(e.keyCode){
20048             case 27: // escape
20049                 this.hide();
20050                 e.preventDefault();
20051                 break;
20052             case 37: // left
20053             case 39: // right
20054                 dir = e.keyCode == 37 ? -1 : 1;
20055                 
20056                 this.vIndex = this.vIndex + dir;
20057                 
20058                 if(this.vIndex < 0){
20059                     this.vIndex = 0;
20060                 }
20061                 
20062                 if(this.vIndex > 11){
20063                     this.vIndex = 11;
20064                 }
20065                 
20066                 if(isNaN(this.vIndex)){
20067                     this.vIndex = 0;
20068                 }
20069                 
20070                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20071                 
20072                 break;
20073             case 38: // up
20074             case 40: // down
20075                 
20076                 dir = e.keyCode == 38 ? -1 : 1;
20077                 
20078                 this.vIndex = this.vIndex + dir * 4;
20079                 
20080                 if(this.vIndex < 0){
20081                     this.vIndex = 0;
20082                 }
20083                 
20084                 if(this.vIndex > 11){
20085                     this.vIndex = 11;
20086                 }
20087                 
20088                 if(isNaN(this.vIndex)){
20089                     this.vIndex = 0;
20090                 }
20091                 
20092                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20093                 break;
20094                 
20095             case 13: // enter
20096                 
20097                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20098                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20099                 }
20100                 
20101                 this.hide();
20102                 e.preventDefault();
20103                 break;
20104             case 9: // tab
20105                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20106                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20107                 }
20108                 this.hide();
20109                 break;
20110             case 16: // shift
20111             case 17: // ctrl
20112             case 18: // alt
20113                 break;
20114             default :
20115                 this.hide();
20116                 
20117         }
20118     },
20119     
20120     remove: function() 
20121     {
20122         this.picker().remove();
20123     }
20124    
20125 });
20126
20127 Roo.apply(Roo.bootstrap.MonthField,  {
20128     
20129     content : {
20130         tag: 'tbody',
20131         cn: [
20132         {
20133             tag: 'tr',
20134             cn: [
20135             {
20136                 tag: 'td',
20137                 colspan: '7'
20138             }
20139             ]
20140         }
20141         ]
20142     },
20143     
20144     dates:{
20145         en: {
20146             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20147             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20148         }
20149     }
20150 });
20151
20152 Roo.apply(Roo.bootstrap.MonthField,  {
20153   
20154     template : {
20155         tag: 'div',
20156         cls: 'datepicker dropdown-menu roo-dynamic',
20157         cn: [
20158             {
20159                 tag: 'div',
20160                 cls: 'datepicker-months',
20161                 cn: [
20162                 {
20163                     tag: 'table',
20164                     cls: 'table-condensed',
20165                     cn:[
20166                         Roo.bootstrap.DateField.content
20167                     ]
20168                 }
20169                 ]
20170             }
20171         ]
20172     }
20173 });
20174
20175  
20176
20177  
20178  /*
20179  * - LGPL
20180  *
20181  * CheckBox
20182  * 
20183  */
20184
20185 /**
20186  * @class Roo.bootstrap.CheckBox
20187  * @extends Roo.bootstrap.Input
20188  * Bootstrap CheckBox class
20189  * 
20190  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20191  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20192  * @cfg {String} boxLabel The text that appears beside the checkbox
20193  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20194  * @cfg {Boolean} checked initnal the element
20195  * @cfg {Boolean} inline inline the element (default false)
20196  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20197  * @cfg {String} tooltip label tooltip
20198  * 
20199  * @constructor
20200  * Create a new CheckBox
20201  * @param {Object} config The config object
20202  */
20203
20204 Roo.bootstrap.CheckBox = function(config){
20205     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20206    
20207     this.addEvents({
20208         /**
20209         * @event check
20210         * Fires when the element is checked or unchecked.
20211         * @param {Roo.bootstrap.CheckBox} this This input
20212         * @param {Boolean} checked The new checked value
20213         */
20214        check : true,
20215        /**
20216         * @event click
20217         * Fires when the element is click.
20218         * @param {Roo.bootstrap.CheckBox} this This input
20219         */
20220        click : true
20221     });
20222     
20223 };
20224
20225 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20226   
20227     inputType: 'checkbox',
20228     inputValue: 1,
20229     valueOff: 0,
20230     boxLabel: false,
20231     checked: false,
20232     weight : false,
20233     inline: false,
20234     tooltip : '',
20235     
20236     getAutoCreate : function()
20237     {
20238         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20239         
20240         var id = Roo.id();
20241         
20242         var cfg = {};
20243         
20244         cfg.cls = 'form-group ' + this.inputType; //input-group
20245         
20246         if(this.inline){
20247             cfg.cls += ' ' + this.inputType + '-inline';
20248         }
20249         
20250         var input =  {
20251             tag: 'input',
20252             id : id,
20253             type : this.inputType,
20254             value : this.inputValue,
20255             cls : 'roo-' + this.inputType, //'form-box',
20256             placeholder : this.placeholder || ''
20257             
20258         };
20259         
20260         if(this.inputType != 'radio'){
20261             var hidden =  {
20262                 tag: 'input',
20263                 type : 'hidden',
20264                 cls : 'roo-hidden-value',
20265                 value : this.checked ? this.inputValue : this.valueOff
20266             };
20267         }
20268         
20269             
20270         if (this.weight) { // Validity check?
20271             cfg.cls += " " + this.inputType + "-" + this.weight;
20272         }
20273         
20274         if (this.disabled) {
20275             input.disabled=true;
20276         }
20277         
20278         if(this.checked){
20279             input.checked = this.checked;
20280         }
20281         
20282         if (this.name) {
20283             
20284             input.name = this.name;
20285             
20286             if(this.inputType != 'radio'){
20287                 hidden.name = this.name;
20288                 input.name = '_hidden_' + this.name;
20289             }
20290         }
20291         
20292         if (this.size) {
20293             input.cls += ' input-' + this.size;
20294         }
20295         
20296         var settings=this;
20297         
20298         ['xs','sm','md','lg'].map(function(size){
20299             if (settings[size]) {
20300                 cfg.cls += ' col-' + size + '-' + settings[size];
20301             }
20302         });
20303         
20304         var inputblock = input;
20305          
20306         if (this.before || this.after) {
20307             
20308             inputblock = {
20309                 cls : 'input-group',
20310                 cn :  [] 
20311             };
20312             
20313             if (this.before) {
20314                 inputblock.cn.push({
20315                     tag :'span',
20316                     cls : 'input-group-addon',
20317                     html : this.before
20318                 });
20319             }
20320             
20321             inputblock.cn.push(input);
20322             
20323             if(this.inputType != 'radio'){
20324                 inputblock.cn.push(hidden);
20325             }
20326             
20327             if (this.after) {
20328                 inputblock.cn.push({
20329                     tag :'span',
20330                     cls : 'input-group-addon',
20331                     html : this.after
20332                 });
20333             }
20334             
20335         }
20336         
20337         if (align ==='left' && this.fieldLabel.length) {
20338 //                Roo.log("left and has label");
20339             cfg.cn = [
20340                 {
20341                     tag: 'label',
20342                     'for' :  id,
20343                     cls : 'control-label',
20344                     html : this.fieldLabel
20345                 },
20346                 {
20347                     cls : "", 
20348                     cn: [
20349                         inputblock
20350                     ]
20351                 }
20352             ];
20353             
20354             if(this.labelWidth > 12){
20355                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20356             }
20357             
20358             if(this.labelWidth < 13 && this.labelmd == 0){
20359                 this.labelmd = this.labelWidth;
20360             }
20361             
20362             if(this.labellg > 0){
20363                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20364                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20365             }
20366             
20367             if(this.labelmd > 0){
20368                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20369                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20370             }
20371             
20372             if(this.labelsm > 0){
20373                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20374                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20375             }
20376             
20377             if(this.labelxs > 0){
20378                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20379                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20380             }
20381             
20382         } else if ( this.fieldLabel.length) {
20383 //                Roo.log(" label");
20384                 cfg.cn = [
20385                    
20386                     {
20387                         tag: this.boxLabel ? 'span' : 'label',
20388                         'for': id,
20389                         cls: 'control-label box-input-label',
20390                         //cls : 'input-group-addon',
20391                         html : this.fieldLabel
20392                     },
20393                     
20394                     inputblock
20395                     
20396                 ];
20397
20398         } else {
20399             
20400 //                Roo.log(" no label && no align");
20401                 cfg.cn = [  inputblock ] ;
20402                 
20403                 
20404         }
20405         
20406         if(this.boxLabel){
20407              var boxLabelCfg = {
20408                 tag: 'label',
20409                 //'for': id, // box label is handled by onclick - so no for...
20410                 cls: 'box-label',
20411                 html: this.boxLabel
20412             };
20413             
20414             if(this.tooltip){
20415                 boxLabelCfg.tooltip = this.tooltip;
20416             }
20417              
20418             cfg.cn.push(boxLabelCfg);
20419         }
20420         
20421         if(this.inputType != 'radio'){
20422             cfg.cn.push(hidden);
20423         }
20424         
20425         return cfg;
20426         
20427     },
20428     
20429     /**
20430      * return the real input element.
20431      */
20432     inputEl: function ()
20433     {
20434         return this.el.select('input.roo-' + this.inputType,true).first();
20435     },
20436     hiddenEl: function ()
20437     {
20438         return this.el.select('input.roo-hidden-value',true).first();
20439     },
20440     
20441     labelEl: function()
20442     {
20443         return this.el.select('label.control-label',true).first();
20444     },
20445     /* depricated... */
20446     
20447     label: function()
20448     {
20449         return this.labelEl();
20450     },
20451     
20452     boxLabelEl: function()
20453     {
20454         return this.el.select('label.box-label',true).first();
20455     },
20456     
20457     initEvents : function()
20458     {
20459 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20460         
20461         this.inputEl().on('click', this.onClick,  this);
20462         
20463         if (this.boxLabel) { 
20464             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20465         }
20466         
20467         this.startValue = this.getValue();
20468         
20469         if(this.groupId){
20470             Roo.bootstrap.CheckBox.register(this);
20471         }
20472     },
20473     
20474     onClick : function(e)
20475     {   
20476         if(this.fireEvent('click', this, e) !== false){
20477             this.setChecked(!this.checked);
20478         }
20479         
20480     },
20481     
20482     setChecked : function(state,suppressEvent)
20483     {
20484         this.startValue = this.getValue();
20485
20486         if(this.inputType == 'radio'){
20487             
20488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20489                 e.dom.checked = false;
20490             });
20491             
20492             this.inputEl().dom.checked = true;
20493             
20494             this.inputEl().dom.value = this.inputValue;
20495             
20496             if(suppressEvent !== true){
20497                 this.fireEvent('check', this, true);
20498             }
20499             
20500             this.validate();
20501             
20502             return;
20503         }
20504         
20505         this.checked = state;
20506         
20507         this.inputEl().dom.checked = state;
20508         
20509         
20510         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20511         
20512         if(suppressEvent !== true){
20513             this.fireEvent('check', this, state);
20514         }
20515         
20516         this.validate();
20517     },
20518     
20519     getValue : function()
20520     {
20521         if(this.inputType == 'radio'){
20522             return this.getGroupValue();
20523         }
20524         
20525         return this.hiddenEl().dom.value;
20526         
20527     },
20528     
20529     getGroupValue : function()
20530     {
20531         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20532             return '';
20533         }
20534         
20535         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20536     },
20537     
20538     setValue : function(v,suppressEvent)
20539     {
20540         if(this.inputType == 'radio'){
20541             this.setGroupValue(v, suppressEvent);
20542             return;
20543         }
20544         
20545         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20546         
20547         this.validate();
20548     },
20549     
20550     setGroupValue : function(v, suppressEvent)
20551     {
20552         this.startValue = this.getValue();
20553         
20554         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20555             e.dom.checked = false;
20556             
20557             if(e.dom.value == v){
20558                 e.dom.checked = true;
20559             }
20560         });
20561         
20562         if(suppressEvent !== true){
20563             this.fireEvent('check', this, true);
20564         }
20565
20566         this.validate();
20567         
20568         return;
20569     },
20570     
20571     validate : function()
20572     {
20573         if(
20574                 this.disabled || 
20575                 (this.inputType == 'radio' && this.validateRadio()) ||
20576                 (this.inputType == 'checkbox' && this.validateCheckbox())
20577         ){
20578             this.markValid();
20579             return true;
20580         }
20581         
20582         this.markInvalid();
20583         return false;
20584     },
20585     
20586     validateRadio : function()
20587     {
20588         if(this.allowBlank){
20589             return true;
20590         }
20591         
20592         var valid = false;
20593         
20594         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20595             if(!e.dom.checked){
20596                 return;
20597             }
20598             
20599             valid = true;
20600             
20601             return false;
20602         });
20603         
20604         return valid;
20605     },
20606     
20607     validateCheckbox : function()
20608     {
20609         if(!this.groupId){
20610             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20611             //return (this.getValue() == this.inputValue) ? true : false;
20612         }
20613         
20614         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20615         
20616         if(!group){
20617             return false;
20618         }
20619         
20620         var r = false;
20621         
20622         for(var i in group){
20623             if(group[i].el.isVisible(true)){
20624                 r = false;
20625                 break;
20626             }
20627             
20628             r = true;
20629         }
20630         
20631         for(var i in group){
20632             if(r){
20633                 break;
20634             }
20635             
20636             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20637         }
20638         
20639         return r;
20640     },
20641     
20642     /**
20643      * Mark this field as valid
20644      */
20645     markValid : function()
20646     {
20647         var _this = this;
20648         
20649         this.fireEvent('valid', this);
20650         
20651         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20652         
20653         if(this.groupId){
20654             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20655         }
20656         
20657         if(label){
20658             label.markValid();
20659         }
20660
20661         if(this.inputType == 'radio'){
20662             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20663                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20664                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20665             });
20666             
20667             return;
20668         }
20669
20670         if(!this.groupId){
20671             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20672             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20673             return;
20674         }
20675         
20676         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20677         
20678         if(!group){
20679             return;
20680         }
20681         
20682         for(var i in group){
20683             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20684             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20685         }
20686     },
20687     
20688      /**
20689      * Mark this field as invalid
20690      * @param {String} msg The validation message
20691      */
20692     markInvalid : function(msg)
20693     {
20694         if(this.allowBlank){
20695             return;
20696         }
20697         
20698         var _this = this;
20699         
20700         this.fireEvent('invalid', this, msg);
20701         
20702         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20703         
20704         if(this.groupId){
20705             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20706         }
20707         
20708         if(label){
20709             label.markInvalid();
20710         }
20711             
20712         if(this.inputType == 'radio'){
20713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20714                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20715                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20716             });
20717             
20718             return;
20719         }
20720         
20721         if(!this.groupId){
20722             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20723             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20724             return;
20725         }
20726         
20727         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20728         
20729         if(!group){
20730             return;
20731         }
20732         
20733         for(var i in group){
20734             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20735             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20736         }
20737         
20738     },
20739     
20740     clearInvalid : function()
20741     {
20742         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20743         
20744         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20745         
20746         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20747         
20748         if (label && label.iconEl) {
20749             label.iconEl.removeClass(label.validClass);
20750             label.iconEl.removeClass(label.invalidClass);
20751         }
20752     },
20753     
20754     disable : function()
20755     {
20756         if(this.inputType != 'radio'){
20757             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20758             return;
20759         }
20760         
20761         var _this = this;
20762         
20763         if(this.rendered){
20764             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20765                 _this.getActionEl().addClass(this.disabledClass);
20766                 e.dom.disabled = true;
20767             });
20768         }
20769         
20770         this.disabled = true;
20771         this.fireEvent("disable", this);
20772         return this;
20773     },
20774
20775     enable : function()
20776     {
20777         if(this.inputType != 'radio'){
20778             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20779             return;
20780         }
20781         
20782         var _this = this;
20783         
20784         if(this.rendered){
20785             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20786                 _this.getActionEl().removeClass(this.disabledClass);
20787                 e.dom.disabled = false;
20788             });
20789         }
20790         
20791         this.disabled = false;
20792         this.fireEvent("enable", this);
20793         return this;
20794     },
20795     
20796     setBoxLabel : function(v)
20797     {
20798         this.boxLabel = v;
20799         
20800         if(this.rendered){
20801             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20802         }
20803     }
20804
20805 });
20806
20807 Roo.apply(Roo.bootstrap.CheckBox, {
20808     
20809     groups: {},
20810     
20811      /**
20812     * register a CheckBox Group
20813     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20814     */
20815     register : function(checkbox)
20816     {
20817         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20818             this.groups[checkbox.groupId] = {};
20819         }
20820         
20821         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20822             return;
20823         }
20824         
20825         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20826         
20827     },
20828     /**
20829     * fetch a CheckBox Group based on the group ID
20830     * @param {string} the group ID
20831     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20832     */
20833     get: function(groupId) {
20834         if (typeof(this.groups[groupId]) == 'undefined') {
20835             return false;
20836         }
20837         
20838         return this.groups[groupId] ;
20839     }
20840     
20841     
20842 });
20843 /*
20844  * - LGPL
20845  *
20846  * RadioItem
20847  * 
20848  */
20849
20850 /**
20851  * @class Roo.bootstrap.Radio
20852  * @extends Roo.bootstrap.Component
20853  * Bootstrap Radio class
20854  * @cfg {String} boxLabel - the label associated
20855  * @cfg {String} value - the value of radio
20856  * 
20857  * @constructor
20858  * Create a new Radio
20859  * @param {Object} config The config object
20860  */
20861 Roo.bootstrap.Radio = function(config){
20862     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20863     
20864 };
20865
20866 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20867     
20868     boxLabel : '',
20869     
20870     value : '',
20871     
20872     getAutoCreate : function()
20873     {
20874         var cfg = {
20875             tag : 'div',
20876             cls : 'form-group radio',
20877             cn : [
20878                 {
20879                     tag : 'label',
20880                     cls : 'box-label',
20881                     html : this.boxLabel
20882                 }
20883             ]
20884         };
20885         
20886         return cfg;
20887     },
20888     
20889     initEvents : function() 
20890     {
20891         this.parent().register(this);
20892         
20893         this.el.on('click', this.onClick, this);
20894         
20895     },
20896     
20897     onClick : function()
20898     {
20899         this.setChecked(true);
20900     },
20901     
20902     setChecked : function(state, suppressEvent)
20903     {
20904         this.parent().setValue(this.value, suppressEvent);
20905         
20906     },
20907     
20908     setBoxLabel : function(v)
20909     {
20910         this.boxLabel = v;
20911         
20912         if(this.rendered){
20913             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20914         }
20915     }
20916     
20917 });
20918  
20919
20920  /*
20921  * - LGPL
20922  *
20923  * Input
20924  * 
20925  */
20926
20927 /**
20928  * @class Roo.bootstrap.SecurePass
20929  * @extends Roo.bootstrap.Input
20930  * Bootstrap SecurePass class
20931  *
20932  * 
20933  * @constructor
20934  * Create a new SecurePass
20935  * @param {Object} config The config object
20936  */
20937  
20938 Roo.bootstrap.SecurePass = function (config) {
20939     // these go here, so the translation tool can replace them..
20940     this.errors = {
20941         PwdEmpty: "Please type a password, and then retype it to confirm.",
20942         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20943         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20944         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20945         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20946         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20947         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20948         TooWeak: "Your password is Too Weak."
20949     },
20950     this.meterLabel = "Password strength:";
20951     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20952     this.meterClass = [
20953         "roo-password-meter-tooweak", 
20954         "roo-password-meter-weak", 
20955         "roo-password-meter-medium", 
20956         "roo-password-meter-strong", 
20957         "roo-password-meter-grey"
20958     ];
20959     
20960     this.errors = {};
20961     
20962     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20963 }
20964
20965 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20966     /**
20967      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20968      * {
20969      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20970      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20971      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20972      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20973      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20974      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20975      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20976      * })
20977      */
20978     // private
20979     
20980     meterWidth: 300,
20981     errorMsg :'',    
20982     errors: false,
20983     imageRoot: '/',
20984     /**
20985      * @cfg {String/Object} Label for the strength meter (defaults to
20986      * 'Password strength:')
20987      */
20988     // private
20989     meterLabel: '',
20990     /**
20991      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20992      * ['Weak', 'Medium', 'Strong'])
20993      */
20994     // private    
20995     pwdStrengths: false,    
20996     // private
20997     strength: 0,
20998     // private
20999     _lastPwd: null,
21000     // private
21001     kCapitalLetter: 0,
21002     kSmallLetter: 1,
21003     kDigit: 2,
21004     kPunctuation: 3,
21005     
21006     insecure: false,
21007     // private
21008     initEvents: function ()
21009     {
21010         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21011
21012         if (this.el.is('input[type=password]') && Roo.isSafari) {
21013             this.el.on('keydown', this.SafariOnKeyDown, this);
21014         }
21015
21016         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21017     },
21018     // private
21019     onRender: function (ct, position)
21020     {
21021         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21022         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21023         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21024
21025         this.trigger.createChild({
21026                    cn: [
21027                     {
21028                     //id: 'PwdMeter',
21029                     tag: 'div',
21030                     cls: 'roo-password-meter-grey col-xs-12',
21031                     style: {
21032                         //width: 0,
21033                         //width: this.meterWidth + 'px'                                                
21034                         }
21035                     },
21036                     {                            
21037                          cls: 'roo-password-meter-text'                          
21038                     }
21039                 ]            
21040         });
21041
21042          
21043         if (this.hideTrigger) {
21044             this.trigger.setDisplayed(false);
21045         }
21046         this.setSize(this.width || '', this.height || '');
21047     },
21048     // private
21049     onDestroy: function ()
21050     {
21051         if (this.trigger) {
21052             this.trigger.removeAllListeners();
21053             this.trigger.remove();
21054         }
21055         if (this.wrap) {
21056             this.wrap.remove();
21057         }
21058         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21059     },
21060     // private
21061     checkStrength: function ()
21062     {
21063         var pwd = this.inputEl().getValue();
21064         if (pwd == this._lastPwd) {
21065             return;
21066         }
21067
21068         var strength;
21069         if (this.ClientSideStrongPassword(pwd)) {
21070             strength = 3;
21071         } else if (this.ClientSideMediumPassword(pwd)) {
21072             strength = 2;
21073         } else if (this.ClientSideWeakPassword(pwd)) {
21074             strength = 1;
21075         } else {
21076             strength = 0;
21077         }
21078         
21079         Roo.log('strength1: ' + strength);
21080         
21081         //var pm = this.trigger.child('div/div/div').dom;
21082         var pm = this.trigger.child('div/div');
21083         pm.removeClass(this.meterClass);
21084         pm.addClass(this.meterClass[strength]);
21085                 
21086         
21087         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21088                 
21089         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21090         
21091         this._lastPwd = pwd;
21092     },
21093     reset: function ()
21094     {
21095         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21096         
21097         this._lastPwd = '';
21098         
21099         var pm = this.trigger.child('div/div');
21100         pm.removeClass(this.meterClass);
21101         pm.addClass('roo-password-meter-grey');        
21102         
21103         
21104         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21105         
21106         pt.innerHTML = '';
21107         this.inputEl().dom.type='password';
21108     },
21109     // private
21110     validateValue: function (value)
21111     {
21112         
21113         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21114             return false;
21115         }
21116         if (value.length == 0) {
21117             if (this.allowBlank) {
21118                 this.clearInvalid();
21119                 return true;
21120             }
21121
21122             this.markInvalid(this.errors.PwdEmpty);
21123             this.errorMsg = this.errors.PwdEmpty;
21124             return false;
21125         }
21126         
21127         if(this.insecure){
21128             return true;
21129         }
21130         
21131         if ('[\x21-\x7e]*'.match(value)) {
21132             this.markInvalid(this.errors.PwdBadChar);
21133             this.errorMsg = this.errors.PwdBadChar;
21134             return false;
21135         }
21136         if (value.length < 6) {
21137             this.markInvalid(this.errors.PwdShort);
21138             this.errorMsg = this.errors.PwdShort;
21139             return false;
21140         }
21141         if (value.length > 16) {
21142             this.markInvalid(this.errors.PwdLong);
21143             this.errorMsg = this.errors.PwdLong;
21144             return false;
21145         }
21146         var strength;
21147         if (this.ClientSideStrongPassword(value)) {
21148             strength = 3;
21149         } else if (this.ClientSideMediumPassword(value)) {
21150             strength = 2;
21151         } else if (this.ClientSideWeakPassword(value)) {
21152             strength = 1;
21153         } else {
21154             strength = 0;
21155         }
21156
21157         
21158         if (strength < 2) {
21159             //this.markInvalid(this.errors.TooWeak);
21160             this.errorMsg = this.errors.TooWeak;
21161             //return false;
21162         }
21163         
21164         
21165         console.log('strength2: ' + strength);
21166         
21167         //var pm = this.trigger.child('div/div/div').dom;
21168         
21169         var pm = this.trigger.child('div/div');
21170         pm.removeClass(this.meterClass);
21171         pm.addClass(this.meterClass[strength]);
21172                 
21173         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21174                 
21175         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21176         
21177         this.errorMsg = ''; 
21178         return true;
21179     },
21180     // private
21181     CharacterSetChecks: function (type)
21182     {
21183         this.type = type;
21184         this.fResult = false;
21185     },
21186     // private
21187     isctype: function (character, type)
21188     {
21189         switch (type) {  
21190             case this.kCapitalLetter:
21191                 if (character >= 'A' && character <= 'Z') {
21192                     return true;
21193                 }
21194                 break;
21195             
21196             case this.kSmallLetter:
21197                 if (character >= 'a' && character <= 'z') {
21198                     return true;
21199                 }
21200                 break;
21201             
21202             case this.kDigit:
21203                 if (character >= '0' && character <= '9') {
21204                     return true;
21205                 }
21206                 break;
21207             
21208             case this.kPunctuation:
21209                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21210                     return true;
21211                 }
21212                 break;
21213             
21214             default:
21215                 return false;
21216         }
21217
21218     },
21219     // private
21220     IsLongEnough: function (pwd, size)
21221     {
21222         return !(pwd == null || isNaN(size) || pwd.length < size);
21223     },
21224     // private
21225     SpansEnoughCharacterSets: function (word, nb)
21226     {
21227         if (!this.IsLongEnough(word, nb))
21228         {
21229             return false;
21230         }
21231
21232         var characterSetChecks = new Array(
21233             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21234             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21235         );
21236         
21237         for (var index = 0; index < word.length; ++index) {
21238             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21239                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21240                     characterSetChecks[nCharSet].fResult = true;
21241                     break;
21242                 }
21243             }
21244         }
21245
21246         var nCharSets = 0;
21247         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21248             if (characterSetChecks[nCharSet].fResult) {
21249                 ++nCharSets;
21250             }
21251         }
21252
21253         if (nCharSets < nb) {
21254             return false;
21255         }
21256         return true;
21257     },
21258     // private
21259     ClientSideStrongPassword: function (pwd)
21260     {
21261         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21262     },
21263     // private
21264     ClientSideMediumPassword: function (pwd)
21265     {
21266         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21267     },
21268     // private
21269     ClientSideWeakPassword: function (pwd)
21270     {
21271         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21272     }
21273           
21274 })//<script type="text/javascript">
21275
21276 /*
21277  * Based  Ext JS Library 1.1.1
21278  * Copyright(c) 2006-2007, Ext JS, LLC.
21279  * LGPL
21280  *
21281  */
21282  
21283 /**
21284  * @class Roo.HtmlEditorCore
21285  * @extends Roo.Component
21286  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21287  *
21288  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21289  */
21290
21291 Roo.HtmlEditorCore = function(config){
21292     
21293     
21294     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21295     
21296     
21297     this.addEvents({
21298         /**
21299          * @event initialize
21300          * Fires when the editor is fully initialized (including the iframe)
21301          * @param {Roo.HtmlEditorCore} this
21302          */
21303         initialize: true,
21304         /**
21305          * @event activate
21306          * Fires when the editor is first receives the focus. Any insertion must wait
21307          * until after this event.
21308          * @param {Roo.HtmlEditorCore} this
21309          */
21310         activate: true,
21311          /**
21312          * @event beforesync
21313          * Fires before the textarea is updated with content from the editor iframe. Return false
21314          * to cancel the sync.
21315          * @param {Roo.HtmlEditorCore} this
21316          * @param {String} html
21317          */
21318         beforesync: true,
21319          /**
21320          * @event beforepush
21321          * Fires before the iframe editor is updated with content from the textarea. Return false
21322          * to cancel the push.
21323          * @param {Roo.HtmlEditorCore} this
21324          * @param {String} html
21325          */
21326         beforepush: true,
21327          /**
21328          * @event sync
21329          * Fires when the textarea is updated with content from the editor iframe.
21330          * @param {Roo.HtmlEditorCore} this
21331          * @param {String} html
21332          */
21333         sync: true,
21334          /**
21335          * @event push
21336          * Fires when the iframe editor is updated with content from the textarea.
21337          * @param {Roo.HtmlEditorCore} this
21338          * @param {String} html
21339          */
21340         push: true,
21341         
21342         /**
21343          * @event editorevent
21344          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21345          * @param {Roo.HtmlEditorCore} this
21346          */
21347         editorevent: true
21348         
21349     });
21350     
21351     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21352     
21353     // defaults : white / black...
21354     this.applyBlacklists();
21355     
21356     
21357     
21358 };
21359
21360
21361 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21362
21363
21364      /**
21365      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21366      */
21367     
21368     owner : false,
21369     
21370      /**
21371      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21372      *                        Roo.resizable.
21373      */
21374     resizable : false,
21375      /**
21376      * @cfg {Number} height (in pixels)
21377      */   
21378     height: 300,
21379    /**
21380      * @cfg {Number} width (in pixels)
21381      */   
21382     width: 500,
21383     
21384     /**
21385      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21386      * 
21387      */
21388     stylesheets: false,
21389     
21390     // id of frame..
21391     frameId: false,
21392     
21393     // private properties
21394     validationEvent : false,
21395     deferHeight: true,
21396     initialized : false,
21397     activated : false,
21398     sourceEditMode : false,
21399     onFocus : Roo.emptyFn,
21400     iframePad:3,
21401     hideMode:'offsets',
21402     
21403     clearUp: true,
21404     
21405     // blacklist + whitelisted elements..
21406     black: false,
21407     white: false,
21408      
21409     bodyCls : '',
21410
21411     /**
21412      * Protected method that will not generally be called directly. It
21413      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21414      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21415      */
21416     getDocMarkup : function(){
21417         // body styles..
21418         var st = '';
21419         
21420         // inherit styels from page...?? 
21421         if (this.stylesheets === false) {
21422             
21423             Roo.get(document.head).select('style').each(function(node) {
21424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21425             });
21426             
21427             Roo.get(document.head).select('link').each(function(node) { 
21428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21429             });
21430             
21431         } else if (!this.stylesheets.length) {
21432                 // simple..
21433                 st = '<style type="text/css">' +
21434                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21435                    '</style>';
21436         } else { 
21437             st = '<style type="text/css">' +
21438                     this.stylesheets +
21439                 '</style>';
21440         }
21441         
21442         st +=  '<style type="text/css">' +
21443             'IMG { cursor: pointer } ' +
21444         '</style>';
21445
21446         var cls = 'roo-htmleditor-body';
21447         
21448         if(this.bodyCls.length){
21449             cls += ' ' + this.bodyCls;
21450         }
21451         
21452         return '<html><head>' + st  +
21453             //<style type="text/css">' +
21454             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21455             //'</style>' +
21456             ' </head><body class="' +  cls + '"></body></html>';
21457     },
21458
21459     // private
21460     onRender : function(ct, position)
21461     {
21462         var _t = this;
21463         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21464         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21465         
21466         
21467         this.el.dom.style.border = '0 none';
21468         this.el.dom.setAttribute('tabIndex', -1);
21469         this.el.addClass('x-hidden hide');
21470         
21471         
21472         
21473         if(Roo.isIE){ // fix IE 1px bogus margin
21474             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21475         }
21476        
21477         
21478         this.frameId = Roo.id();
21479         
21480          
21481         
21482         var iframe = this.owner.wrap.createChild({
21483             tag: 'iframe',
21484             cls: 'form-control', // bootstrap..
21485             id: this.frameId,
21486             name: this.frameId,
21487             frameBorder : 'no',
21488             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21489         }, this.el
21490         );
21491         
21492         
21493         this.iframe = iframe.dom;
21494
21495          this.assignDocWin();
21496         
21497         this.doc.designMode = 'on';
21498        
21499         this.doc.open();
21500         this.doc.write(this.getDocMarkup());
21501         this.doc.close();
21502
21503         
21504         var task = { // must defer to wait for browser to be ready
21505             run : function(){
21506                 //console.log("run task?" + this.doc.readyState);
21507                 this.assignDocWin();
21508                 if(this.doc.body || this.doc.readyState == 'complete'){
21509                     try {
21510                         this.doc.designMode="on";
21511                     } catch (e) {
21512                         return;
21513                     }
21514                     Roo.TaskMgr.stop(task);
21515                     this.initEditor.defer(10, this);
21516                 }
21517             },
21518             interval : 10,
21519             duration: 10000,
21520             scope: this
21521         };
21522         Roo.TaskMgr.start(task);
21523
21524     },
21525
21526     // private
21527     onResize : function(w, h)
21528     {
21529          Roo.log('resize: ' +w + ',' + h );
21530         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21531         if(!this.iframe){
21532             return;
21533         }
21534         if(typeof w == 'number'){
21535             
21536             this.iframe.style.width = w + 'px';
21537         }
21538         if(typeof h == 'number'){
21539             
21540             this.iframe.style.height = h + 'px';
21541             if(this.doc){
21542                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21543             }
21544         }
21545         
21546     },
21547
21548     /**
21549      * Toggles the editor between standard and source edit mode.
21550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21551      */
21552     toggleSourceEdit : function(sourceEditMode){
21553         
21554         this.sourceEditMode = sourceEditMode === true;
21555         
21556         if(this.sourceEditMode){
21557  
21558             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21559             
21560         }else{
21561             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21562             //this.iframe.className = '';
21563             this.deferFocus();
21564         }
21565         //this.setSize(this.owner.wrap.getSize());
21566         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21567     },
21568
21569     
21570   
21571
21572     /**
21573      * Protected method that will not generally be called directly. If you need/want
21574      * custom HTML cleanup, this is the method you should override.
21575      * @param {String} html The HTML to be cleaned
21576      * return {String} The cleaned HTML
21577      */
21578     cleanHtml : function(html){
21579         html = String(html);
21580         if(html.length > 5){
21581             if(Roo.isSafari){ // strip safari nonsense
21582                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21583             }
21584         }
21585         if(html == '&nbsp;'){
21586             html = '';
21587         }
21588         return html;
21589     },
21590
21591     /**
21592      * HTML Editor -> Textarea
21593      * Protected method that will not generally be called directly. Syncs the contents
21594      * of the editor iframe with the textarea.
21595      */
21596     syncValue : function(){
21597         if(this.initialized){
21598             var bd = (this.doc.body || this.doc.documentElement);
21599             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21600             var html = bd.innerHTML;
21601             if(Roo.isSafari){
21602                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21603                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21604                 if(m && m[1]){
21605                     html = '<div style="'+m[0]+'">' + html + '</div>';
21606                 }
21607             }
21608             html = this.cleanHtml(html);
21609             // fix up the special chars.. normaly like back quotes in word...
21610             // however we do not want to do this with chinese..
21611             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21612                 var cc = b.charCodeAt();
21613                 if (
21614                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21615                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21616                     (cc >= 0xf900 && cc < 0xfb00 )
21617                 ) {
21618                         return b;
21619                 }
21620                 return "&#"+cc+";" 
21621             });
21622             if(this.owner.fireEvent('beforesync', this, html) !== false){
21623                 this.el.dom.value = html;
21624                 this.owner.fireEvent('sync', this, html);
21625             }
21626         }
21627     },
21628
21629     /**
21630      * Protected method that will not generally be called directly. Pushes the value of the textarea
21631      * into the iframe editor.
21632      */
21633     pushValue : function(){
21634         if(this.initialized){
21635             var v = this.el.dom.value.trim();
21636             
21637 //            if(v.length < 1){
21638 //                v = '&#160;';
21639 //            }
21640             
21641             if(this.owner.fireEvent('beforepush', this, v) !== false){
21642                 var d = (this.doc.body || this.doc.documentElement);
21643                 d.innerHTML = v;
21644                 this.cleanUpPaste();
21645                 this.el.dom.value = d.innerHTML;
21646                 this.owner.fireEvent('push', this, v);
21647             }
21648         }
21649     },
21650
21651     // private
21652     deferFocus : function(){
21653         this.focus.defer(10, this);
21654     },
21655
21656     // doc'ed in Field
21657     focus : function(){
21658         if(this.win && !this.sourceEditMode){
21659             this.win.focus();
21660         }else{
21661             this.el.focus();
21662         }
21663     },
21664     
21665     assignDocWin: function()
21666     {
21667         var iframe = this.iframe;
21668         
21669          if(Roo.isIE){
21670             this.doc = iframe.contentWindow.document;
21671             this.win = iframe.contentWindow;
21672         } else {
21673 //            if (!Roo.get(this.frameId)) {
21674 //                return;
21675 //            }
21676 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21677 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21678             
21679             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21680                 return;
21681             }
21682             
21683             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21684             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21685         }
21686     },
21687     
21688     // private
21689     initEditor : function(){
21690         //console.log("INIT EDITOR");
21691         this.assignDocWin();
21692         
21693         
21694         
21695         this.doc.designMode="on";
21696         this.doc.open();
21697         this.doc.write(this.getDocMarkup());
21698         this.doc.close();
21699         
21700         var dbody = (this.doc.body || this.doc.documentElement);
21701         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21702         // this copies styles from the containing element into thsi one..
21703         // not sure why we need all of this..
21704         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21705         
21706         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21707         //ss['background-attachment'] = 'fixed'; // w3c
21708         dbody.bgProperties = 'fixed'; // ie
21709         //Roo.DomHelper.applyStyles(dbody, ss);
21710         Roo.EventManager.on(this.doc, {
21711             //'mousedown': this.onEditorEvent,
21712             'mouseup': this.onEditorEvent,
21713             'dblclick': this.onEditorEvent,
21714             'click': this.onEditorEvent,
21715             'keyup': this.onEditorEvent,
21716             buffer:100,
21717             scope: this
21718         });
21719         if(Roo.isGecko){
21720             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21721         }
21722         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21723             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21724         }
21725         this.initialized = true;
21726
21727         this.owner.fireEvent('initialize', this);
21728         this.pushValue();
21729     },
21730
21731     // private
21732     onDestroy : function(){
21733         
21734         
21735         
21736         if(this.rendered){
21737             
21738             //for (var i =0; i < this.toolbars.length;i++) {
21739             //    // fixme - ask toolbars for heights?
21740             //    this.toolbars[i].onDestroy();
21741            // }
21742             
21743             //this.wrap.dom.innerHTML = '';
21744             //this.wrap.remove();
21745         }
21746     },
21747
21748     // private
21749     onFirstFocus : function(){
21750         
21751         this.assignDocWin();
21752         
21753         
21754         this.activated = true;
21755          
21756     
21757         if(Roo.isGecko){ // prevent silly gecko errors
21758             this.win.focus();
21759             var s = this.win.getSelection();
21760             if(!s.focusNode || s.focusNode.nodeType != 3){
21761                 var r = s.getRangeAt(0);
21762                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21763                 r.collapse(true);
21764                 this.deferFocus();
21765             }
21766             try{
21767                 this.execCmd('useCSS', true);
21768                 this.execCmd('styleWithCSS', false);
21769             }catch(e){}
21770         }
21771         this.owner.fireEvent('activate', this);
21772     },
21773
21774     // private
21775     adjustFont: function(btn){
21776         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21777         //if(Roo.isSafari){ // safari
21778         //    adjust *= 2;
21779        // }
21780         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21781         if(Roo.isSafari){ // safari
21782             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21783             v =  (v < 10) ? 10 : v;
21784             v =  (v > 48) ? 48 : v;
21785             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21786             
21787         }
21788         
21789         
21790         v = Math.max(1, v+adjust);
21791         
21792         this.execCmd('FontSize', v  );
21793     },
21794
21795     onEditorEvent : function(e)
21796     {
21797         this.owner.fireEvent('editorevent', this, e);
21798       //  this.updateToolbar();
21799         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21800     },
21801
21802     insertTag : function(tg)
21803     {
21804         // could be a bit smarter... -> wrap the current selected tRoo..
21805         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21806             
21807             range = this.createRange(this.getSelection());
21808             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21809             wrappingNode.appendChild(range.extractContents());
21810             range.insertNode(wrappingNode);
21811
21812             return;
21813             
21814             
21815             
21816         }
21817         this.execCmd("formatblock",   tg);
21818         
21819     },
21820     
21821     insertText : function(txt)
21822     {
21823         
21824         
21825         var range = this.createRange();
21826         range.deleteContents();
21827                //alert(Sender.getAttribute('label'));
21828                
21829         range.insertNode(this.doc.createTextNode(txt));
21830     } ,
21831     
21832      
21833
21834     /**
21835      * Executes a Midas editor command on the editor document and performs necessary focus and
21836      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21837      * @param {String} cmd The Midas command
21838      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21839      */
21840     relayCmd : function(cmd, value){
21841         this.win.focus();
21842         this.execCmd(cmd, value);
21843         this.owner.fireEvent('editorevent', this);
21844         //this.updateToolbar();
21845         this.owner.deferFocus();
21846     },
21847
21848     /**
21849      * Executes a Midas editor command directly on the editor document.
21850      * For visual commands, you should use {@link #relayCmd} instead.
21851      * <b>This should only be called after the editor is initialized.</b>
21852      * @param {String} cmd The Midas command
21853      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21854      */
21855     execCmd : function(cmd, value){
21856         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21857         this.syncValue();
21858     },
21859  
21860  
21861    
21862     /**
21863      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21864      * to insert tRoo.
21865      * @param {String} text | dom node.. 
21866      */
21867     insertAtCursor : function(text)
21868     {
21869         
21870         if(!this.activated){
21871             return;
21872         }
21873         /*
21874         if(Roo.isIE){
21875             this.win.focus();
21876             var r = this.doc.selection.createRange();
21877             if(r){
21878                 r.collapse(true);
21879                 r.pasteHTML(text);
21880                 this.syncValue();
21881                 this.deferFocus();
21882             
21883             }
21884             return;
21885         }
21886         */
21887         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21888             this.win.focus();
21889             
21890             
21891             // from jquery ui (MIT licenced)
21892             var range, node;
21893             var win = this.win;
21894             
21895             if (win.getSelection && win.getSelection().getRangeAt) {
21896                 range = win.getSelection().getRangeAt(0);
21897                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21898                 range.insertNode(node);
21899             } else if (win.document.selection && win.document.selection.createRange) {
21900                 // no firefox support
21901                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21902                 win.document.selection.createRange().pasteHTML(txt);
21903             } else {
21904                 // no firefox support
21905                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21906                 this.execCmd('InsertHTML', txt);
21907             } 
21908             
21909             this.syncValue();
21910             
21911             this.deferFocus();
21912         }
21913     },
21914  // private
21915     mozKeyPress : function(e){
21916         if(e.ctrlKey){
21917             var c = e.getCharCode(), cmd;
21918           
21919             if(c > 0){
21920                 c = String.fromCharCode(c).toLowerCase();
21921                 switch(c){
21922                     case 'b':
21923                         cmd = 'bold';
21924                         break;
21925                     case 'i':
21926                         cmd = 'italic';
21927                         break;
21928                     
21929                     case 'u':
21930                         cmd = 'underline';
21931                         break;
21932                     
21933                     case 'v':
21934                         this.cleanUpPaste.defer(100, this);
21935                         return;
21936                         
21937                 }
21938                 if(cmd){
21939                     this.win.focus();
21940                     this.execCmd(cmd);
21941                     this.deferFocus();
21942                     e.preventDefault();
21943                 }
21944                 
21945             }
21946         }
21947     },
21948
21949     // private
21950     fixKeys : function(){ // load time branching for fastest keydown performance
21951         if(Roo.isIE){
21952             return function(e){
21953                 var k = e.getKey(), r;
21954                 if(k == e.TAB){
21955                     e.stopEvent();
21956                     r = this.doc.selection.createRange();
21957                     if(r){
21958                         r.collapse(true);
21959                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21960                         this.deferFocus();
21961                     }
21962                     return;
21963                 }
21964                 
21965                 if(k == e.ENTER){
21966                     r = this.doc.selection.createRange();
21967                     if(r){
21968                         var target = r.parentElement();
21969                         if(!target || target.tagName.toLowerCase() != 'li'){
21970                             e.stopEvent();
21971                             r.pasteHTML('<br />');
21972                             r.collapse(false);
21973                             r.select();
21974                         }
21975                     }
21976                 }
21977                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21978                     this.cleanUpPaste.defer(100, this);
21979                     return;
21980                 }
21981                 
21982                 
21983             };
21984         }else if(Roo.isOpera){
21985             return function(e){
21986                 var k = e.getKey();
21987                 if(k == e.TAB){
21988                     e.stopEvent();
21989                     this.win.focus();
21990                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21991                     this.deferFocus();
21992                 }
21993                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21994                     this.cleanUpPaste.defer(100, this);
21995                     return;
21996                 }
21997                 
21998             };
21999         }else if(Roo.isSafari){
22000             return function(e){
22001                 var k = e.getKey();
22002                 
22003                 if(k == e.TAB){
22004                     e.stopEvent();
22005                     this.execCmd('InsertText','\t');
22006                     this.deferFocus();
22007                     return;
22008                 }
22009                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22010                     this.cleanUpPaste.defer(100, this);
22011                     return;
22012                 }
22013                 
22014              };
22015         }
22016     }(),
22017     
22018     getAllAncestors: function()
22019     {
22020         var p = this.getSelectedNode();
22021         var a = [];
22022         if (!p) {
22023             a.push(p); // push blank onto stack..
22024             p = this.getParentElement();
22025         }
22026         
22027         
22028         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22029             a.push(p);
22030             p = p.parentNode;
22031         }
22032         a.push(this.doc.body);
22033         return a;
22034     },
22035     lastSel : false,
22036     lastSelNode : false,
22037     
22038     
22039     getSelection : function() 
22040     {
22041         this.assignDocWin();
22042         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22043     },
22044     
22045     getSelectedNode: function() 
22046     {
22047         // this may only work on Gecko!!!
22048         
22049         // should we cache this!!!!
22050         
22051         
22052         
22053          
22054         var range = this.createRange(this.getSelection()).cloneRange();
22055         
22056         if (Roo.isIE) {
22057             var parent = range.parentElement();
22058             while (true) {
22059                 var testRange = range.duplicate();
22060                 testRange.moveToElementText(parent);
22061                 if (testRange.inRange(range)) {
22062                     break;
22063                 }
22064                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22065                     break;
22066                 }
22067                 parent = parent.parentElement;
22068             }
22069             return parent;
22070         }
22071         
22072         // is ancestor a text element.
22073         var ac =  range.commonAncestorContainer;
22074         if (ac.nodeType == 3) {
22075             ac = ac.parentNode;
22076         }
22077         
22078         var ar = ac.childNodes;
22079          
22080         var nodes = [];
22081         var other_nodes = [];
22082         var has_other_nodes = false;
22083         for (var i=0;i<ar.length;i++) {
22084             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22085                 continue;
22086             }
22087             // fullly contained node.
22088             
22089             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22090                 nodes.push(ar[i]);
22091                 continue;
22092             }
22093             
22094             // probably selected..
22095             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22096                 other_nodes.push(ar[i]);
22097                 continue;
22098             }
22099             // outer..
22100             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22101                 continue;
22102             }
22103             
22104             
22105             has_other_nodes = true;
22106         }
22107         if (!nodes.length && other_nodes.length) {
22108             nodes= other_nodes;
22109         }
22110         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22111             return false;
22112         }
22113         
22114         return nodes[0];
22115     },
22116     createRange: function(sel)
22117     {
22118         // this has strange effects when using with 
22119         // top toolbar - not sure if it's a great idea.
22120         //this.editor.contentWindow.focus();
22121         if (typeof sel != "undefined") {
22122             try {
22123                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22124             } catch(e) {
22125                 return this.doc.createRange();
22126             }
22127         } else {
22128             return this.doc.createRange();
22129         }
22130     },
22131     getParentElement: function()
22132     {
22133         
22134         this.assignDocWin();
22135         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22136         
22137         var range = this.createRange(sel);
22138          
22139         try {
22140             var p = range.commonAncestorContainer;
22141             while (p.nodeType == 3) { // text node
22142                 p = p.parentNode;
22143             }
22144             return p;
22145         } catch (e) {
22146             return null;
22147         }
22148     
22149     },
22150     /***
22151      *
22152      * Range intersection.. the hard stuff...
22153      *  '-1' = before
22154      *  '0' = hits..
22155      *  '1' = after.
22156      *         [ -- selected range --- ]
22157      *   [fail]                        [fail]
22158      *
22159      *    basically..
22160      *      if end is before start or  hits it. fail.
22161      *      if start is after end or hits it fail.
22162      *
22163      *   if either hits (but other is outside. - then it's not 
22164      *   
22165      *    
22166      **/
22167     
22168     
22169     // @see http://www.thismuchiknow.co.uk/?p=64.
22170     rangeIntersectsNode : function(range, node)
22171     {
22172         var nodeRange = node.ownerDocument.createRange();
22173         try {
22174             nodeRange.selectNode(node);
22175         } catch (e) {
22176             nodeRange.selectNodeContents(node);
22177         }
22178     
22179         var rangeStartRange = range.cloneRange();
22180         rangeStartRange.collapse(true);
22181     
22182         var rangeEndRange = range.cloneRange();
22183         rangeEndRange.collapse(false);
22184     
22185         var nodeStartRange = nodeRange.cloneRange();
22186         nodeStartRange.collapse(true);
22187     
22188         var nodeEndRange = nodeRange.cloneRange();
22189         nodeEndRange.collapse(false);
22190     
22191         return rangeStartRange.compareBoundaryPoints(
22192                  Range.START_TO_START, nodeEndRange) == -1 &&
22193                rangeEndRange.compareBoundaryPoints(
22194                  Range.START_TO_START, nodeStartRange) == 1;
22195         
22196          
22197     },
22198     rangeCompareNode : function(range, node)
22199     {
22200         var nodeRange = node.ownerDocument.createRange();
22201         try {
22202             nodeRange.selectNode(node);
22203         } catch (e) {
22204             nodeRange.selectNodeContents(node);
22205         }
22206         
22207         
22208         range.collapse(true);
22209     
22210         nodeRange.collapse(true);
22211      
22212         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22213         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22214          
22215         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22216         
22217         var nodeIsBefore   =  ss == 1;
22218         var nodeIsAfter    = ee == -1;
22219         
22220         if (nodeIsBefore && nodeIsAfter) {
22221             return 0; // outer
22222         }
22223         if (!nodeIsBefore && nodeIsAfter) {
22224             return 1; //right trailed.
22225         }
22226         
22227         if (nodeIsBefore && !nodeIsAfter) {
22228             return 2;  // left trailed.
22229         }
22230         // fully contined.
22231         return 3;
22232     },
22233
22234     // private? - in a new class?
22235     cleanUpPaste :  function()
22236     {
22237         // cleans up the whole document..
22238         Roo.log('cleanuppaste');
22239         
22240         this.cleanUpChildren(this.doc.body);
22241         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22242         if (clean != this.doc.body.innerHTML) {
22243             this.doc.body.innerHTML = clean;
22244         }
22245         
22246     },
22247     
22248     cleanWordChars : function(input) {// change the chars to hex code
22249         var he = Roo.HtmlEditorCore;
22250         
22251         var output = input;
22252         Roo.each(he.swapCodes, function(sw) { 
22253             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22254             
22255             output = output.replace(swapper, sw[1]);
22256         });
22257         
22258         return output;
22259     },
22260     
22261     
22262     cleanUpChildren : function (n)
22263     {
22264         if (!n.childNodes.length) {
22265             return;
22266         }
22267         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22268            this.cleanUpChild(n.childNodes[i]);
22269         }
22270     },
22271     
22272     
22273         
22274     
22275     cleanUpChild : function (node)
22276     {
22277         var ed = this;
22278         //console.log(node);
22279         if (node.nodeName == "#text") {
22280             // clean up silly Windows -- stuff?
22281             return; 
22282         }
22283         if (node.nodeName == "#comment") {
22284             node.parentNode.removeChild(node);
22285             // clean up silly Windows -- stuff?
22286             return; 
22287         }
22288         var lcname = node.tagName.toLowerCase();
22289         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22290         // whitelist of tags..
22291         
22292         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22293             // remove node.
22294             node.parentNode.removeChild(node);
22295             return;
22296             
22297         }
22298         
22299         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22300         
22301         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22302         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22303         
22304         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22305         //    remove_keep_children = true;
22306         //}
22307         
22308         if (remove_keep_children) {
22309             this.cleanUpChildren(node);
22310             // inserts everything just before this node...
22311             while (node.childNodes.length) {
22312                 var cn = node.childNodes[0];
22313                 node.removeChild(cn);
22314                 node.parentNode.insertBefore(cn, node);
22315             }
22316             node.parentNode.removeChild(node);
22317             return;
22318         }
22319         
22320         if (!node.attributes || !node.attributes.length) {
22321             this.cleanUpChildren(node);
22322             return;
22323         }
22324         
22325         function cleanAttr(n,v)
22326         {
22327             
22328             if (v.match(/^\./) || v.match(/^\//)) {
22329                 return;
22330             }
22331             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22332                 return;
22333             }
22334             if (v.match(/^#/)) {
22335                 return;
22336             }
22337 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22338             node.removeAttribute(n);
22339             
22340         }
22341         
22342         var cwhite = this.cwhite;
22343         var cblack = this.cblack;
22344             
22345         function cleanStyle(n,v)
22346         {
22347             if (v.match(/expression/)) { //XSS?? should we even bother..
22348                 node.removeAttribute(n);
22349                 return;
22350             }
22351             
22352             var parts = v.split(/;/);
22353             var clean = [];
22354             
22355             Roo.each(parts, function(p) {
22356                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22357                 if (!p.length) {
22358                     return true;
22359                 }
22360                 var l = p.split(':').shift().replace(/\s+/g,'');
22361                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22362                 
22363                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22364 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22365                     //node.removeAttribute(n);
22366                     return true;
22367                 }
22368                 //Roo.log()
22369                 // only allow 'c whitelisted system attributes'
22370                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22371 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22372                     //node.removeAttribute(n);
22373                     return true;
22374                 }
22375                 
22376                 
22377                  
22378                 
22379                 clean.push(p);
22380                 return true;
22381             });
22382             if (clean.length) { 
22383                 node.setAttribute(n, clean.join(';'));
22384             } else {
22385                 node.removeAttribute(n);
22386             }
22387             
22388         }
22389         
22390         
22391         for (var i = node.attributes.length-1; i > -1 ; i--) {
22392             var a = node.attributes[i];
22393             //console.log(a);
22394             
22395             if (a.name.toLowerCase().substr(0,2)=='on')  {
22396                 node.removeAttribute(a.name);
22397                 continue;
22398             }
22399             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22400                 node.removeAttribute(a.name);
22401                 continue;
22402             }
22403             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22404                 cleanAttr(a.name,a.value); // fixme..
22405                 continue;
22406             }
22407             if (a.name == 'style') {
22408                 cleanStyle(a.name,a.value);
22409                 continue;
22410             }
22411             /// clean up MS crap..
22412             // tecnically this should be a list of valid class'es..
22413             
22414             
22415             if (a.name == 'class') {
22416                 if (a.value.match(/^Mso/)) {
22417                     node.className = '';
22418                 }
22419                 
22420                 if (a.value.match(/^body$/)) {
22421                     node.className = '';
22422                 }
22423                 continue;
22424             }
22425             
22426             // style cleanup!?
22427             // class cleanup?
22428             
22429         }
22430         
22431         
22432         this.cleanUpChildren(node);
22433         
22434         
22435     },
22436     
22437     /**
22438      * Clean up MS wordisms...
22439      */
22440     cleanWord : function(node)
22441     {
22442         
22443         
22444         if (!node) {
22445             this.cleanWord(this.doc.body);
22446             return;
22447         }
22448         if (node.nodeName == "#text") {
22449             // clean up silly Windows -- stuff?
22450             return; 
22451         }
22452         if (node.nodeName == "#comment") {
22453             node.parentNode.removeChild(node);
22454             // clean up silly Windows -- stuff?
22455             return; 
22456         }
22457         
22458         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22459             node.parentNode.removeChild(node);
22460             return;
22461         }
22462         
22463         // remove - but keep children..
22464         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22465             while (node.childNodes.length) {
22466                 var cn = node.childNodes[0];
22467                 node.removeChild(cn);
22468                 node.parentNode.insertBefore(cn, node);
22469             }
22470             node.parentNode.removeChild(node);
22471             this.iterateChildren(node, this.cleanWord);
22472             return;
22473         }
22474         // clean styles
22475         if (node.className.length) {
22476             
22477             var cn = node.className.split(/\W+/);
22478             var cna = [];
22479             Roo.each(cn, function(cls) {
22480                 if (cls.match(/Mso[a-zA-Z]+/)) {
22481                     return;
22482                 }
22483                 cna.push(cls);
22484             });
22485             node.className = cna.length ? cna.join(' ') : '';
22486             if (!cna.length) {
22487                 node.removeAttribute("class");
22488             }
22489         }
22490         
22491         if (node.hasAttribute("lang")) {
22492             node.removeAttribute("lang");
22493         }
22494         
22495         if (node.hasAttribute("style")) {
22496             
22497             var styles = node.getAttribute("style").split(";");
22498             var nstyle = [];
22499             Roo.each(styles, function(s) {
22500                 if (!s.match(/:/)) {
22501                     return;
22502                 }
22503                 var kv = s.split(":");
22504                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22505                     return;
22506                 }
22507                 // what ever is left... we allow.
22508                 nstyle.push(s);
22509             });
22510             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22511             if (!nstyle.length) {
22512                 node.removeAttribute('style');
22513             }
22514         }
22515         this.iterateChildren(node, this.cleanWord);
22516         
22517         
22518         
22519     },
22520     /**
22521      * iterateChildren of a Node, calling fn each time, using this as the scole..
22522      * @param {DomNode} node node to iterate children of.
22523      * @param {Function} fn method of this class to call on each item.
22524      */
22525     iterateChildren : function(node, fn)
22526     {
22527         if (!node.childNodes.length) {
22528                 return;
22529         }
22530         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22531            fn.call(this, node.childNodes[i])
22532         }
22533     },
22534     
22535     
22536     /**
22537      * cleanTableWidths.
22538      *
22539      * Quite often pasting from word etc.. results in tables with column and widths.
22540      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22541      *
22542      */
22543     cleanTableWidths : function(node)
22544     {
22545          
22546          
22547         if (!node) {
22548             this.cleanTableWidths(this.doc.body);
22549             return;
22550         }
22551         
22552         // ignore list...
22553         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22554             return; 
22555         }
22556         Roo.log(node.tagName);
22557         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22558             this.iterateChildren(node, this.cleanTableWidths);
22559             return;
22560         }
22561         if (node.hasAttribute('width')) {
22562             node.removeAttribute('width');
22563         }
22564         
22565          
22566         if (node.hasAttribute("style")) {
22567             // pretty basic...
22568             
22569             var styles = node.getAttribute("style").split(";");
22570             var nstyle = [];
22571             Roo.each(styles, function(s) {
22572                 if (!s.match(/:/)) {
22573                     return;
22574                 }
22575                 var kv = s.split(":");
22576                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22577                     return;
22578                 }
22579                 // what ever is left... we allow.
22580                 nstyle.push(s);
22581             });
22582             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22583             if (!nstyle.length) {
22584                 node.removeAttribute('style');
22585             }
22586         }
22587         
22588         this.iterateChildren(node, this.cleanTableWidths);
22589         
22590         
22591     },
22592     
22593     
22594     
22595     
22596     domToHTML : function(currentElement, depth, nopadtext) {
22597         
22598         depth = depth || 0;
22599         nopadtext = nopadtext || false;
22600     
22601         if (!currentElement) {
22602             return this.domToHTML(this.doc.body);
22603         }
22604         
22605         //Roo.log(currentElement);
22606         var j;
22607         var allText = false;
22608         var nodeName = currentElement.nodeName;
22609         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22610         
22611         if  (nodeName == '#text') {
22612             
22613             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22614         }
22615         
22616         
22617         var ret = '';
22618         if (nodeName != 'BODY') {
22619              
22620             var i = 0;
22621             // Prints the node tagName, such as <A>, <IMG>, etc
22622             if (tagName) {
22623                 var attr = [];
22624                 for(i = 0; i < currentElement.attributes.length;i++) {
22625                     // quoting?
22626                     var aname = currentElement.attributes.item(i).name;
22627                     if (!currentElement.attributes.item(i).value.length) {
22628                         continue;
22629                     }
22630                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22631                 }
22632                 
22633                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22634             } 
22635             else {
22636                 
22637                 // eack
22638             }
22639         } else {
22640             tagName = false;
22641         }
22642         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22643             return ret;
22644         }
22645         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22646             nopadtext = true;
22647         }
22648         
22649         
22650         // Traverse the tree
22651         i = 0;
22652         var currentElementChild = currentElement.childNodes.item(i);
22653         var allText = true;
22654         var innerHTML  = '';
22655         lastnode = '';
22656         while (currentElementChild) {
22657             // Formatting code (indent the tree so it looks nice on the screen)
22658             var nopad = nopadtext;
22659             if (lastnode == 'SPAN') {
22660                 nopad  = true;
22661             }
22662             // text
22663             if  (currentElementChild.nodeName == '#text') {
22664                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22665                 toadd = nopadtext ? toadd : toadd.trim();
22666                 if (!nopad && toadd.length > 80) {
22667                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22668                 }
22669                 innerHTML  += toadd;
22670                 
22671                 i++;
22672                 currentElementChild = currentElement.childNodes.item(i);
22673                 lastNode = '';
22674                 continue;
22675             }
22676             allText = false;
22677             
22678             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22679                 
22680             // Recursively traverse the tree structure of the child node
22681             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22682             lastnode = currentElementChild.nodeName;
22683             i++;
22684             currentElementChild=currentElement.childNodes.item(i);
22685         }
22686         
22687         ret += innerHTML;
22688         
22689         if (!allText) {
22690                 // The remaining code is mostly for formatting the tree
22691             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22692         }
22693         
22694         
22695         if (tagName) {
22696             ret+= "</"+tagName+">";
22697         }
22698         return ret;
22699         
22700     },
22701         
22702     applyBlacklists : function()
22703     {
22704         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22705         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22706         
22707         this.white = [];
22708         this.black = [];
22709         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22710             if (b.indexOf(tag) > -1) {
22711                 return;
22712             }
22713             this.white.push(tag);
22714             
22715         }, this);
22716         
22717         Roo.each(w, function(tag) {
22718             if (b.indexOf(tag) > -1) {
22719                 return;
22720             }
22721             if (this.white.indexOf(tag) > -1) {
22722                 return;
22723             }
22724             this.white.push(tag);
22725             
22726         }, this);
22727         
22728         
22729         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22730             if (w.indexOf(tag) > -1) {
22731                 return;
22732             }
22733             this.black.push(tag);
22734             
22735         }, this);
22736         
22737         Roo.each(b, function(tag) {
22738             if (w.indexOf(tag) > -1) {
22739                 return;
22740             }
22741             if (this.black.indexOf(tag) > -1) {
22742                 return;
22743             }
22744             this.black.push(tag);
22745             
22746         }, this);
22747         
22748         
22749         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22750         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22751         
22752         this.cwhite = [];
22753         this.cblack = [];
22754         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22755             if (b.indexOf(tag) > -1) {
22756                 return;
22757             }
22758             this.cwhite.push(tag);
22759             
22760         }, this);
22761         
22762         Roo.each(w, function(tag) {
22763             if (b.indexOf(tag) > -1) {
22764                 return;
22765             }
22766             if (this.cwhite.indexOf(tag) > -1) {
22767                 return;
22768             }
22769             this.cwhite.push(tag);
22770             
22771         }, this);
22772         
22773         
22774         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22775             if (w.indexOf(tag) > -1) {
22776                 return;
22777             }
22778             this.cblack.push(tag);
22779             
22780         }, this);
22781         
22782         Roo.each(b, function(tag) {
22783             if (w.indexOf(tag) > -1) {
22784                 return;
22785             }
22786             if (this.cblack.indexOf(tag) > -1) {
22787                 return;
22788             }
22789             this.cblack.push(tag);
22790             
22791         }, this);
22792     },
22793     
22794     setStylesheets : function(stylesheets)
22795     {
22796         if(typeof(stylesheets) == 'string'){
22797             Roo.get(this.iframe.contentDocument.head).createChild({
22798                 tag : 'link',
22799                 rel : 'stylesheet',
22800                 type : 'text/css',
22801                 href : stylesheets
22802             });
22803             
22804             return;
22805         }
22806         var _this = this;
22807      
22808         Roo.each(stylesheets, function(s) {
22809             if(!s.length){
22810                 return;
22811             }
22812             
22813             Roo.get(_this.iframe.contentDocument.head).createChild({
22814                 tag : 'link',
22815                 rel : 'stylesheet',
22816                 type : 'text/css',
22817                 href : s
22818             });
22819         });
22820
22821         
22822     },
22823     
22824     removeStylesheets : function()
22825     {
22826         var _this = this;
22827         
22828         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22829             s.remove();
22830         });
22831     },
22832     
22833     setStyle : function(style)
22834     {
22835         Roo.get(this.iframe.contentDocument.head).createChild({
22836             tag : 'style',
22837             type : 'text/css',
22838             html : style
22839         });
22840
22841         return;
22842     }
22843     
22844     // hide stuff that is not compatible
22845     /**
22846      * @event blur
22847      * @hide
22848      */
22849     /**
22850      * @event change
22851      * @hide
22852      */
22853     /**
22854      * @event focus
22855      * @hide
22856      */
22857     /**
22858      * @event specialkey
22859      * @hide
22860      */
22861     /**
22862      * @cfg {String} fieldClass @hide
22863      */
22864     /**
22865      * @cfg {String} focusClass @hide
22866      */
22867     /**
22868      * @cfg {String} autoCreate @hide
22869      */
22870     /**
22871      * @cfg {String} inputType @hide
22872      */
22873     /**
22874      * @cfg {String} invalidClass @hide
22875      */
22876     /**
22877      * @cfg {String} invalidText @hide
22878      */
22879     /**
22880      * @cfg {String} msgFx @hide
22881      */
22882     /**
22883      * @cfg {String} validateOnBlur @hide
22884      */
22885 });
22886
22887 Roo.HtmlEditorCore.white = [
22888         'area', 'br', 'img', 'input', 'hr', 'wbr',
22889         
22890        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22891        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22892        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22893        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22894        'table',   'ul',         'xmp', 
22895        
22896        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22897       'thead',   'tr', 
22898      
22899       'dir', 'menu', 'ol', 'ul', 'dl',
22900        
22901       'embed',  'object'
22902 ];
22903
22904
22905 Roo.HtmlEditorCore.black = [
22906     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22907         'applet', // 
22908         'base',   'basefont', 'bgsound', 'blink',  'body', 
22909         'frame',  'frameset', 'head',    'html',   'ilayer', 
22910         'iframe', 'layer',  'link',     'meta',    'object',   
22911         'script', 'style' ,'title',  'xml' // clean later..
22912 ];
22913 Roo.HtmlEditorCore.clean = [
22914     'script', 'style', 'title', 'xml'
22915 ];
22916 Roo.HtmlEditorCore.remove = [
22917     'font'
22918 ];
22919 // attributes..
22920
22921 Roo.HtmlEditorCore.ablack = [
22922     'on'
22923 ];
22924     
22925 Roo.HtmlEditorCore.aclean = [ 
22926     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22927 ];
22928
22929 // protocols..
22930 Roo.HtmlEditorCore.pwhite= [
22931         'http',  'https',  'mailto'
22932 ];
22933
22934 // white listed style attributes.
22935 Roo.HtmlEditorCore.cwhite= [
22936       //  'text-align', /// default is to allow most things..
22937       
22938          
22939 //        'font-size'//??
22940 ];
22941
22942 // black listed style attributes.
22943 Roo.HtmlEditorCore.cblack= [
22944       //  'font-size' -- this can be set by the project 
22945 ];
22946
22947
22948 Roo.HtmlEditorCore.swapCodes   =[ 
22949     [    8211, "--" ], 
22950     [    8212, "--" ], 
22951     [    8216,  "'" ],  
22952     [    8217, "'" ],  
22953     [    8220, '"' ],  
22954     [    8221, '"' ],  
22955     [    8226, "*" ],  
22956     [    8230, "..." ]
22957 ]; 
22958
22959     /*
22960  * - LGPL
22961  *
22962  * HtmlEditor
22963  * 
22964  */
22965
22966 /**
22967  * @class Roo.bootstrap.HtmlEditor
22968  * @extends Roo.bootstrap.TextArea
22969  * Bootstrap HtmlEditor class
22970
22971  * @constructor
22972  * Create a new HtmlEditor
22973  * @param {Object} config The config object
22974  */
22975
22976 Roo.bootstrap.HtmlEditor = function(config){
22977     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22978     if (!this.toolbars) {
22979         this.toolbars = [];
22980     }
22981     
22982     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22983     this.addEvents({
22984             /**
22985              * @event initialize
22986              * Fires when the editor is fully initialized (including the iframe)
22987              * @param {HtmlEditor} this
22988              */
22989             initialize: true,
22990             /**
22991              * @event activate
22992              * Fires when the editor is first receives the focus. Any insertion must wait
22993              * until after this event.
22994              * @param {HtmlEditor} this
22995              */
22996             activate: true,
22997              /**
22998              * @event beforesync
22999              * Fires before the textarea is updated with content from the editor iframe. Return false
23000              * to cancel the sync.
23001              * @param {HtmlEditor} this
23002              * @param {String} html
23003              */
23004             beforesync: true,
23005              /**
23006              * @event beforepush
23007              * Fires before the iframe editor is updated with content from the textarea. Return false
23008              * to cancel the push.
23009              * @param {HtmlEditor} this
23010              * @param {String} html
23011              */
23012             beforepush: true,
23013              /**
23014              * @event sync
23015              * Fires when the textarea is updated with content from the editor iframe.
23016              * @param {HtmlEditor} this
23017              * @param {String} html
23018              */
23019             sync: true,
23020              /**
23021              * @event push
23022              * Fires when the iframe editor is updated with content from the textarea.
23023              * @param {HtmlEditor} this
23024              * @param {String} html
23025              */
23026             push: true,
23027              /**
23028              * @event editmodechange
23029              * Fires when the editor switches edit modes
23030              * @param {HtmlEditor} this
23031              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23032              */
23033             editmodechange: true,
23034             /**
23035              * @event editorevent
23036              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23037              * @param {HtmlEditor} this
23038              */
23039             editorevent: true,
23040             /**
23041              * @event firstfocus
23042              * Fires when on first focus - needed by toolbars..
23043              * @param {HtmlEditor} this
23044              */
23045             firstfocus: true,
23046             /**
23047              * @event autosave
23048              * Auto save the htmlEditor value as a file into Events
23049              * @param {HtmlEditor} this
23050              */
23051             autosave: true,
23052             /**
23053              * @event savedpreview
23054              * preview the saved version of htmlEditor
23055              * @param {HtmlEditor} this
23056              */
23057             savedpreview: true
23058         });
23059 };
23060
23061
23062 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23063     
23064     
23065       /**
23066      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23067      */
23068     toolbars : false,
23069     
23070      /**
23071     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23072     */
23073     btns : [],
23074    
23075      /**
23076      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23077      *                        Roo.resizable.
23078      */
23079     resizable : false,
23080      /**
23081      * @cfg {Number} height (in pixels)
23082      */   
23083     height: 300,
23084    /**
23085      * @cfg {Number} width (in pixels)
23086      */   
23087     width: false,
23088     
23089     /**
23090      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23091      * 
23092      */
23093     stylesheets: false,
23094     
23095     // id of frame..
23096     frameId: false,
23097     
23098     // private properties
23099     validationEvent : false,
23100     deferHeight: true,
23101     initialized : false,
23102     activated : false,
23103     
23104     onFocus : Roo.emptyFn,
23105     iframePad:3,
23106     hideMode:'offsets',
23107     
23108     tbContainer : false,
23109     
23110     bodyCls : '',
23111     
23112     toolbarContainer :function() {
23113         return this.wrap.select('.x-html-editor-tb',true).first();
23114     },
23115
23116     /**
23117      * Protected method that will not generally be called directly. It
23118      * is called when the editor creates its toolbar. Override this method if you need to
23119      * add custom toolbar buttons.
23120      * @param {HtmlEditor} editor
23121      */
23122     createToolbar : function(){
23123         Roo.log('renewing');
23124         Roo.log("create toolbars");
23125         
23126         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23127         this.toolbars[0].render(this.toolbarContainer());
23128         
23129         return;
23130         
23131 //        if (!editor.toolbars || !editor.toolbars.length) {
23132 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23133 //        }
23134 //        
23135 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23136 //            editor.toolbars[i] = Roo.factory(
23137 //                    typeof(editor.toolbars[i]) == 'string' ?
23138 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23139 //                Roo.bootstrap.HtmlEditor);
23140 //            editor.toolbars[i].init(editor);
23141 //        }
23142     },
23143
23144      
23145     // private
23146     onRender : function(ct, position)
23147     {
23148        // Roo.log("Call onRender: " + this.xtype);
23149         var _t = this;
23150         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23151       
23152         this.wrap = this.inputEl().wrap({
23153             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23154         });
23155         
23156         this.editorcore.onRender(ct, position);
23157          
23158         if (this.resizable) {
23159             this.resizeEl = new Roo.Resizable(this.wrap, {
23160                 pinned : true,
23161                 wrap: true,
23162                 dynamic : true,
23163                 minHeight : this.height,
23164                 height: this.height,
23165                 handles : this.resizable,
23166                 width: this.width,
23167                 listeners : {
23168                     resize : function(r, w, h) {
23169                         _t.onResize(w,h); // -something
23170                     }
23171                 }
23172             });
23173             
23174         }
23175         this.createToolbar(this);
23176        
23177         
23178         if(!this.width && this.resizable){
23179             this.setSize(this.wrap.getSize());
23180         }
23181         if (this.resizeEl) {
23182             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23183             // should trigger onReize..
23184         }
23185         
23186     },
23187
23188     // private
23189     onResize : function(w, h)
23190     {
23191         Roo.log('resize: ' +w + ',' + h );
23192         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23193         var ew = false;
23194         var eh = false;
23195         
23196         if(this.inputEl() ){
23197             if(typeof w == 'number'){
23198                 var aw = w - this.wrap.getFrameWidth('lr');
23199                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23200                 ew = aw;
23201             }
23202             if(typeof h == 'number'){
23203                  var tbh = -11;  // fixme it needs to tool bar size!
23204                 for (var i =0; i < this.toolbars.length;i++) {
23205                     // fixme - ask toolbars for heights?
23206                     tbh += this.toolbars[i].el.getHeight();
23207                     //if (this.toolbars[i].footer) {
23208                     //    tbh += this.toolbars[i].footer.el.getHeight();
23209                     //}
23210                 }
23211               
23212                 
23213                 
23214                 
23215                 
23216                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23217                 ah -= 5; // knock a few pixes off for look..
23218                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23219                 var eh = ah;
23220             }
23221         }
23222         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23223         this.editorcore.onResize(ew,eh);
23224         
23225     },
23226
23227     /**
23228      * Toggles the editor between standard and source edit mode.
23229      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23230      */
23231     toggleSourceEdit : function(sourceEditMode)
23232     {
23233         this.editorcore.toggleSourceEdit(sourceEditMode);
23234         
23235         if(this.editorcore.sourceEditMode){
23236             Roo.log('editor - showing textarea');
23237             
23238 //            Roo.log('in');
23239 //            Roo.log(this.syncValue());
23240             this.syncValue();
23241             this.inputEl().removeClass(['hide', 'x-hidden']);
23242             this.inputEl().dom.removeAttribute('tabIndex');
23243             this.inputEl().focus();
23244         }else{
23245             Roo.log('editor - hiding textarea');
23246 //            Roo.log('out')
23247 //            Roo.log(this.pushValue()); 
23248             this.pushValue();
23249             
23250             this.inputEl().addClass(['hide', 'x-hidden']);
23251             this.inputEl().dom.setAttribute('tabIndex', -1);
23252             //this.deferFocus();
23253         }
23254          
23255         if(this.resizable){
23256             this.setSize(this.wrap.getSize());
23257         }
23258         
23259         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23260     },
23261  
23262     // private (for BoxComponent)
23263     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23264
23265     // private (for BoxComponent)
23266     getResizeEl : function(){
23267         return this.wrap;
23268     },
23269
23270     // private (for BoxComponent)
23271     getPositionEl : function(){
23272         return this.wrap;
23273     },
23274
23275     // private
23276     initEvents : function(){
23277         this.originalValue = this.getValue();
23278     },
23279
23280 //    /**
23281 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23282 //     * @method
23283 //     */
23284 //    markInvalid : Roo.emptyFn,
23285 //    /**
23286 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23287 //     * @method
23288 //     */
23289 //    clearInvalid : Roo.emptyFn,
23290
23291     setValue : function(v){
23292         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23293         this.editorcore.pushValue();
23294     },
23295
23296      
23297     // private
23298     deferFocus : function(){
23299         this.focus.defer(10, this);
23300     },
23301
23302     // doc'ed in Field
23303     focus : function(){
23304         this.editorcore.focus();
23305         
23306     },
23307       
23308
23309     // private
23310     onDestroy : function(){
23311         
23312         
23313         
23314         if(this.rendered){
23315             
23316             for (var i =0; i < this.toolbars.length;i++) {
23317                 // fixme - ask toolbars for heights?
23318                 this.toolbars[i].onDestroy();
23319             }
23320             
23321             this.wrap.dom.innerHTML = '';
23322             this.wrap.remove();
23323         }
23324     },
23325
23326     // private
23327     onFirstFocus : function(){
23328         //Roo.log("onFirstFocus");
23329         this.editorcore.onFirstFocus();
23330          for (var i =0; i < this.toolbars.length;i++) {
23331             this.toolbars[i].onFirstFocus();
23332         }
23333         
23334     },
23335     
23336     // private
23337     syncValue : function()
23338     {   
23339         this.editorcore.syncValue();
23340     },
23341     
23342     pushValue : function()
23343     {   
23344         this.editorcore.pushValue();
23345     }
23346      
23347     
23348     // hide stuff that is not compatible
23349     /**
23350      * @event blur
23351      * @hide
23352      */
23353     /**
23354      * @event change
23355      * @hide
23356      */
23357     /**
23358      * @event focus
23359      * @hide
23360      */
23361     /**
23362      * @event specialkey
23363      * @hide
23364      */
23365     /**
23366      * @cfg {String} fieldClass @hide
23367      */
23368     /**
23369      * @cfg {String} focusClass @hide
23370      */
23371     /**
23372      * @cfg {String} autoCreate @hide
23373      */
23374     /**
23375      * @cfg {String} inputType @hide
23376      */
23377     /**
23378      * @cfg {String} invalidClass @hide
23379      */
23380     /**
23381      * @cfg {String} invalidText @hide
23382      */
23383     /**
23384      * @cfg {String} msgFx @hide
23385      */
23386     /**
23387      * @cfg {String} validateOnBlur @hide
23388      */
23389 });
23390  
23391     
23392    
23393    
23394    
23395       
23396 Roo.namespace('Roo.bootstrap.htmleditor');
23397 /**
23398  * @class Roo.bootstrap.HtmlEditorToolbar1
23399  * Basic Toolbar
23400  * 
23401  * Usage:
23402  *
23403  new Roo.bootstrap.HtmlEditor({
23404     ....
23405     toolbars : [
23406         new Roo.bootstrap.HtmlEditorToolbar1({
23407             disable : { fonts: 1 , format: 1, ..., ... , ...],
23408             btns : [ .... ]
23409         })
23410     }
23411      
23412  * 
23413  * @cfg {Object} disable List of elements to disable..
23414  * @cfg {Array} btns List of additional buttons.
23415  * 
23416  * 
23417  * NEEDS Extra CSS? 
23418  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23419  */
23420  
23421 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23422 {
23423     
23424     Roo.apply(this, config);
23425     
23426     // default disabled, based on 'good practice'..
23427     this.disable = this.disable || {};
23428     Roo.applyIf(this.disable, {
23429         fontSize : true,
23430         colors : true,
23431         specialElements : true
23432     });
23433     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23434     
23435     this.editor = config.editor;
23436     this.editorcore = config.editor.editorcore;
23437     
23438     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23439     
23440     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23441     // dont call parent... till later.
23442 }
23443 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23444      
23445     bar : true,
23446     
23447     editor : false,
23448     editorcore : false,
23449     
23450     
23451     formats : [
23452         "p" ,  
23453         "h1","h2","h3","h4","h5","h6", 
23454         "pre", "code", 
23455         "abbr", "acronym", "address", "cite", "samp", "var",
23456         'div','span'
23457     ],
23458     
23459     onRender : function(ct, position)
23460     {
23461        // Roo.log("Call onRender: " + this.xtype);
23462         
23463        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23464        Roo.log(this.el);
23465        this.el.dom.style.marginBottom = '0';
23466        var _this = this;
23467        var editorcore = this.editorcore;
23468        var editor= this.editor;
23469        
23470        var children = [];
23471        var btn = function(id,cmd , toggle, handler, html){
23472        
23473             var  event = toggle ? 'toggle' : 'click';
23474        
23475             var a = {
23476                 size : 'sm',
23477                 xtype: 'Button',
23478                 xns: Roo.bootstrap,
23479                 glyphicon : id,
23480                 cmd : id || cmd,
23481                 enableToggle:toggle !== false,
23482                 html : html || '',
23483                 pressed : toggle ? false : null,
23484                 listeners : {}
23485             };
23486             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23487                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23488             };
23489             children.push(a);
23490             return a;
23491        }
23492        
23493     //    var cb_box = function...
23494         
23495         var style = {
23496                 xtype: 'Button',
23497                 size : 'sm',
23498                 xns: Roo.bootstrap,
23499                 glyphicon : 'font',
23500                 //html : 'submit'
23501                 menu : {
23502                     xtype: 'Menu',
23503                     xns: Roo.bootstrap,
23504                     items:  []
23505                 }
23506         };
23507         Roo.each(this.formats, function(f) {
23508             style.menu.items.push({
23509                 xtype :'MenuItem',
23510                 xns: Roo.bootstrap,
23511                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23512                 tagname : f,
23513                 listeners : {
23514                     click : function()
23515                     {
23516                         editorcore.insertTag(this.tagname);
23517                         editor.focus();
23518                     }
23519                 }
23520                 
23521             });
23522         });
23523         children.push(style);   
23524         
23525         btn('bold',false,true);
23526         btn('italic',false,true);
23527         btn('align-left', 'justifyleft',true);
23528         btn('align-center', 'justifycenter',true);
23529         btn('align-right' , 'justifyright',true);
23530         btn('link', false, false, function(btn) {
23531             //Roo.log("create link?");
23532             var url = prompt(this.createLinkText, this.defaultLinkValue);
23533             if(url && url != 'http:/'+'/'){
23534                 this.editorcore.relayCmd('createlink', url);
23535             }
23536         }),
23537         btn('list','insertunorderedlist',true);
23538         btn('pencil', false,true, function(btn){
23539                 Roo.log(this);
23540                 this.toggleSourceEdit(btn.pressed);
23541         });
23542         
23543         if (this.editor.btns.length > 0) {
23544             for (var i = 0; i<this.editor.btns.length; i++) {
23545                 children.push(this.editor.btns[i]);
23546             }
23547         }
23548         
23549         /*
23550         var cog = {
23551                 xtype: 'Button',
23552                 size : 'sm',
23553                 xns: Roo.bootstrap,
23554                 glyphicon : 'cog',
23555                 //html : 'submit'
23556                 menu : {
23557                     xtype: 'Menu',
23558                     xns: Roo.bootstrap,
23559                     items:  []
23560                 }
23561         };
23562         
23563         cog.menu.items.push({
23564             xtype :'MenuItem',
23565             xns: Roo.bootstrap,
23566             html : Clean styles,
23567             tagname : f,
23568             listeners : {
23569                 click : function()
23570                 {
23571                     editorcore.insertTag(this.tagname);
23572                     editor.focus();
23573                 }
23574             }
23575             
23576         });
23577        */
23578         
23579          
23580        this.xtype = 'NavSimplebar';
23581         
23582         for(var i=0;i< children.length;i++) {
23583             
23584             this.buttons.add(this.addxtypeChild(children[i]));
23585             
23586         }
23587         
23588         editor.on('editorevent', this.updateToolbar, this);
23589     },
23590     onBtnClick : function(id)
23591     {
23592        this.editorcore.relayCmd(id);
23593        this.editorcore.focus();
23594     },
23595     
23596     /**
23597      * Protected method that will not generally be called directly. It triggers
23598      * a toolbar update by reading the markup state of the current selection in the editor.
23599      */
23600     updateToolbar: function(){
23601
23602         if(!this.editorcore.activated){
23603             this.editor.onFirstFocus(); // is this neeed?
23604             return;
23605         }
23606
23607         var btns = this.buttons; 
23608         var doc = this.editorcore.doc;
23609         btns.get('bold').setActive(doc.queryCommandState('bold'));
23610         btns.get('italic').setActive(doc.queryCommandState('italic'));
23611         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23612         
23613         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23614         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23615         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23616         
23617         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23618         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23619          /*
23620         
23621         var ans = this.editorcore.getAllAncestors();
23622         if (this.formatCombo) {
23623             
23624             
23625             var store = this.formatCombo.store;
23626             this.formatCombo.setValue("");
23627             for (var i =0; i < ans.length;i++) {
23628                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23629                     // select it..
23630                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23631                     break;
23632                 }
23633             }
23634         }
23635         
23636         
23637         
23638         // hides menus... - so this cant be on a menu...
23639         Roo.bootstrap.MenuMgr.hideAll();
23640         */
23641         Roo.bootstrap.MenuMgr.hideAll();
23642         //this.editorsyncValue();
23643     },
23644     onFirstFocus: function() {
23645         this.buttons.each(function(item){
23646            item.enable();
23647         });
23648     },
23649     toggleSourceEdit : function(sourceEditMode){
23650         
23651           
23652         if(sourceEditMode){
23653             Roo.log("disabling buttons");
23654            this.buttons.each( function(item){
23655                 if(item.cmd != 'pencil'){
23656                     item.disable();
23657                 }
23658             });
23659           
23660         }else{
23661             Roo.log("enabling buttons");
23662             if(this.editorcore.initialized){
23663                 this.buttons.each( function(item){
23664                     item.enable();
23665                 });
23666             }
23667             
23668         }
23669         Roo.log("calling toggole on editor");
23670         // tell the editor that it's been pressed..
23671         this.editor.toggleSourceEdit(sourceEditMode);
23672        
23673     }
23674 });
23675
23676
23677
23678
23679
23680 /**
23681  * @class Roo.bootstrap.Table.AbstractSelectionModel
23682  * @extends Roo.util.Observable
23683  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23684  * implemented by descendant classes.  This class should not be directly instantiated.
23685  * @constructor
23686  */
23687 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23688     this.locked = false;
23689     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23690 };
23691
23692
23693 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23694     /** @ignore Called by the grid automatically. Do not call directly. */
23695     init : function(grid){
23696         this.grid = grid;
23697         this.initEvents();
23698     },
23699
23700     /**
23701      * Locks the selections.
23702      */
23703     lock : function(){
23704         this.locked = true;
23705     },
23706
23707     /**
23708      * Unlocks the selections.
23709      */
23710     unlock : function(){
23711         this.locked = false;
23712     },
23713
23714     /**
23715      * Returns true if the selections are locked.
23716      * @return {Boolean}
23717      */
23718     isLocked : function(){
23719         return this.locked;
23720     }
23721 });
23722 /**
23723  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23724  * @class Roo.bootstrap.Table.RowSelectionModel
23725  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23726  * It supports multiple selections and keyboard selection/navigation. 
23727  * @constructor
23728  * @param {Object} config
23729  */
23730
23731 Roo.bootstrap.Table.RowSelectionModel = function(config){
23732     Roo.apply(this, config);
23733     this.selections = new Roo.util.MixedCollection(false, function(o){
23734         return o.id;
23735     });
23736
23737     this.last = false;
23738     this.lastActive = false;
23739
23740     this.addEvents({
23741         /**
23742              * @event selectionchange
23743              * Fires when the selection changes
23744              * @param {SelectionModel} this
23745              */
23746             "selectionchange" : true,
23747         /**
23748              * @event afterselectionchange
23749              * Fires after the selection changes (eg. by key press or clicking)
23750              * @param {SelectionModel} this
23751              */
23752             "afterselectionchange" : true,
23753         /**
23754              * @event beforerowselect
23755              * Fires when a row is selected being selected, return false to cancel.
23756              * @param {SelectionModel} this
23757              * @param {Number} rowIndex The selected index
23758              * @param {Boolean} keepExisting False if other selections will be cleared
23759              */
23760             "beforerowselect" : true,
23761         /**
23762              * @event rowselect
23763              * Fires when a row is selected.
23764              * @param {SelectionModel} this
23765              * @param {Number} rowIndex The selected index
23766              * @param {Roo.data.Record} r The record
23767              */
23768             "rowselect" : true,
23769         /**
23770              * @event rowdeselect
23771              * Fires when a row is deselected.
23772              * @param {SelectionModel} this
23773              * @param {Number} rowIndex The selected index
23774              */
23775         "rowdeselect" : true
23776     });
23777     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23778     this.locked = false;
23779  };
23780
23781 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23782     /**
23783      * @cfg {Boolean} singleSelect
23784      * True to allow selection of only one row at a time (defaults to false)
23785      */
23786     singleSelect : false,
23787
23788     // private
23789     initEvents : function()
23790     {
23791
23792         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23793         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23794         //}else{ // allow click to work like normal
23795          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23796         //}
23797         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23798         this.grid.on("rowclick", this.handleMouseDown, this);
23799         
23800         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23801             "up" : function(e){
23802                 if(!e.shiftKey){
23803                     this.selectPrevious(e.shiftKey);
23804                 }else if(this.last !== false && this.lastActive !== false){
23805                     var last = this.last;
23806                     this.selectRange(this.last,  this.lastActive-1);
23807                     this.grid.getView().focusRow(this.lastActive);
23808                     if(last !== false){
23809                         this.last = last;
23810                     }
23811                 }else{
23812                     this.selectFirstRow();
23813                 }
23814                 this.fireEvent("afterselectionchange", this);
23815             },
23816             "down" : function(e){
23817                 if(!e.shiftKey){
23818                     this.selectNext(e.shiftKey);
23819                 }else if(this.last !== false && this.lastActive !== false){
23820                     var last = this.last;
23821                     this.selectRange(this.last,  this.lastActive+1);
23822                     this.grid.getView().focusRow(this.lastActive);
23823                     if(last !== false){
23824                         this.last = last;
23825                     }
23826                 }else{
23827                     this.selectFirstRow();
23828                 }
23829                 this.fireEvent("afterselectionchange", this);
23830             },
23831             scope: this
23832         });
23833         this.grid.store.on('load', function(){
23834             this.selections.clear();
23835         },this);
23836         /*
23837         var view = this.grid.view;
23838         view.on("refresh", this.onRefresh, this);
23839         view.on("rowupdated", this.onRowUpdated, this);
23840         view.on("rowremoved", this.onRemove, this);
23841         */
23842     },
23843
23844     // private
23845     onRefresh : function()
23846     {
23847         var ds = this.grid.store, i, v = this.grid.view;
23848         var s = this.selections;
23849         s.each(function(r){
23850             if((i = ds.indexOfId(r.id)) != -1){
23851                 v.onRowSelect(i);
23852             }else{
23853                 s.remove(r);
23854             }
23855         });
23856     },
23857
23858     // private
23859     onRemove : function(v, index, r){
23860         this.selections.remove(r);
23861     },
23862
23863     // private
23864     onRowUpdated : function(v, index, r){
23865         if(this.isSelected(r)){
23866             v.onRowSelect(index);
23867         }
23868     },
23869
23870     /**
23871      * Select records.
23872      * @param {Array} records The records to select
23873      * @param {Boolean} keepExisting (optional) True to keep existing selections
23874      */
23875     selectRecords : function(records, keepExisting)
23876     {
23877         if(!keepExisting){
23878             this.clearSelections();
23879         }
23880             var ds = this.grid.store;
23881         for(var i = 0, len = records.length; i < len; i++){
23882             this.selectRow(ds.indexOf(records[i]), true);
23883         }
23884     },
23885
23886     /**
23887      * Gets the number of selected rows.
23888      * @return {Number}
23889      */
23890     getCount : function(){
23891         return this.selections.length;
23892     },
23893
23894     /**
23895      * Selects the first row in the grid.
23896      */
23897     selectFirstRow : function(){
23898         this.selectRow(0);
23899     },
23900
23901     /**
23902      * Select the last row.
23903      * @param {Boolean} keepExisting (optional) True to keep existing selections
23904      */
23905     selectLastRow : function(keepExisting){
23906         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23907         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23908     },
23909
23910     /**
23911      * Selects the row immediately following the last selected row.
23912      * @param {Boolean} keepExisting (optional) True to keep existing selections
23913      */
23914     selectNext : function(keepExisting)
23915     {
23916             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23917             this.selectRow(this.last+1, keepExisting);
23918             this.grid.getView().focusRow(this.last);
23919         }
23920     },
23921
23922     /**
23923      * Selects the row that precedes the last selected row.
23924      * @param {Boolean} keepExisting (optional) True to keep existing selections
23925      */
23926     selectPrevious : function(keepExisting){
23927         if(this.last){
23928             this.selectRow(this.last-1, keepExisting);
23929             this.grid.getView().focusRow(this.last);
23930         }
23931     },
23932
23933     /**
23934      * Returns the selected records
23935      * @return {Array} Array of selected records
23936      */
23937     getSelections : function(){
23938         return [].concat(this.selections.items);
23939     },
23940
23941     /**
23942      * Returns the first selected record.
23943      * @return {Record}
23944      */
23945     getSelected : function(){
23946         return this.selections.itemAt(0);
23947     },
23948
23949
23950     /**
23951      * Clears all selections.
23952      */
23953     clearSelections : function(fast)
23954     {
23955         if(this.locked) {
23956             return;
23957         }
23958         if(fast !== true){
23959                 var ds = this.grid.store;
23960             var s = this.selections;
23961             s.each(function(r){
23962                 this.deselectRow(ds.indexOfId(r.id));
23963             }, this);
23964             s.clear();
23965         }else{
23966             this.selections.clear();
23967         }
23968         this.last = false;
23969     },
23970
23971
23972     /**
23973      * Selects all rows.
23974      */
23975     selectAll : function(){
23976         if(this.locked) {
23977             return;
23978         }
23979         this.selections.clear();
23980         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23981             this.selectRow(i, true);
23982         }
23983     },
23984
23985     /**
23986      * Returns True if there is a selection.
23987      * @return {Boolean}
23988      */
23989     hasSelection : function(){
23990         return this.selections.length > 0;
23991     },
23992
23993     /**
23994      * Returns True if the specified row is selected.
23995      * @param {Number/Record} record The record or index of the record to check
23996      * @return {Boolean}
23997      */
23998     isSelected : function(index){
23999             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24000         return (r && this.selections.key(r.id) ? true : false);
24001     },
24002
24003     /**
24004      * Returns True if the specified record id is selected.
24005      * @param {String} id The id of record to check
24006      * @return {Boolean}
24007      */
24008     isIdSelected : function(id){
24009         return (this.selections.key(id) ? true : false);
24010     },
24011
24012
24013     // private
24014     handleMouseDBClick : function(e, t){
24015         
24016     },
24017     // private
24018     handleMouseDown : function(e, t)
24019     {
24020             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24021         if(this.isLocked() || rowIndex < 0 ){
24022             return;
24023         };
24024         if(e.shiftKey && this.last !== false){
24025             var last = this.last;
24026             this.selectRange(last, rowIndex, e.ctrlKey);
24027             this.last = last; // reset the last
24028             t.focus();
24029     
24030         }else{
24031             var isSelected = this.isSelected(rowIndex);
24032             //Roo.log("select row:" + rowIndex);
24033             if(isSelected){
24034                 this.deselectRow(rowIndex);
24035             } else {
24036                         this.selectRow(rowIndex, true);
24037             }
24038     
24039             /*
24040                 if(e.button !== 0 && isSelected){
24041                 alert('rowIndex 2: ' + rowIndex);
24042                     view.focusRow(rowIndex);
24043                 }else if(e.ctrlKey && isSelected){
24044                     this.deselectRow(rowIndex);
24045                 }else if(!isSelected){
24046                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24047                     view.focusRow(rowIndex);
24048                 }
24049             */
24050         }
24051         this.fireEvent("afterselectionchange", this);
24052     },
24053     // private
24054     handleDragableRowClick :  function(grid, rowIndex, e) 
24055     {
24056         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24057             this.selectRow(rowIndex, false);
24058             grid.view.focusRow(rowIndex);
24059              this.fireEvent("afterselectionchange", this);
24060         }
24061     },
24062     
24063     /**
24064      * Selects multiple rows.
24065      * @param {Array} rows Array of the indexes of the row to select
24066      * @param {Boolean} keepExisting (optional) True to keep existing selections
24067      */
24068     selectRows : function(rows, keepExisting){
24069         if(!keepExisting){
24070             this.clearSelections();
24071         }
24072         for(var i = 0, len = rows.length; i < len; i++){
24073             this.selectRow(rows[i], true);
24074         }
24075     },
24076
24077     /**
24078      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24079      * @param {Number} startRow The index of the first row in the range
24080      * @param {Number} endRow The index of the last row in the range
24081      * @param {Boolean} keepExisting (optional) True to retain existing selections
24082      */
24083     selectRange : function(startRow, endRow, keepExisting){
24084         if(this.locked) {
24085             return;
24086         }
24087         if(!keepExisting){
24088             this.clearSelections();
24089         }
24090         if(startRow <= endRow){
24091             for(var i = startRow; i <= endRow; i++){
24092                 this.selectRow(i, true);
24093             }
24094         }else{
24095             for(var i = startRow; i >= endRow; i--){
24096                 this.selectRow(i, true);
24097             }
24098         }
24099     },
24100
24101     /**
24102      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24103      * @param {Number} startRow The index of the first row in the range
24104      * @param {Number} endRow The index of the last row in the range
24105      */
24106     deselectRange : function(startRow, endRow, preventViewNotify){
24107         if(this.locked) {
24108             return;
24109         }
24110         for(var i = startRow; i <= endRow; i++){
24111             this.deselectRow(i, preventViewNotify);
24112         }
24113     },
24114
24115     /**
24116      * Selects a row.
24117      * @param {Number} row The index of the row to select
24118      * @param {Boolean} keepExisting (optional) True to keep existing selections
24119      */
24120     selectRow : function(index, keepExisting, preventViewNotify)
24121     {
24122             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24123             return;
24124         }
24125         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24126             if(!keepExisting || this.singleSelect){
24127                 this.clearSelections();
24128             }
24129             
24130             var r = this.grid.store.getAt(index);
24131             //console.log('selectRow - record id :' + r.id);
24132             
24133             this.selections.add(r);
24134             this.last = this.lastActive = index;
24135             if(!preventViewNotify){
24136                 var proxy = new Roo.Element(
24137                                 this.grid.getRowDom(index)
24138                 );
24139                 proxy.addClass('bg-info info');
24140             }
24141             this.fireEvent("rowselect", this, index, r);
24142             this.fireEvent("selectionchange", this);
24143         }
24144     },
24145
24146     /**
24147      * Deselects a row.
24148      * @param {Number} row The index of the row to deselect
24149      */
24150     deselectRow : function(index, preventViewNotify)
24151     {
24152         if(this.locked) {
24153             return;
24154         }
24155         if(this.last == index){
24156             this.last = false;
24157         }
24158         if(this.lastActive == index){
24159             this.lastActive = false;
24160         }
24161         
24162         var r = this.grid.store.getAt(index);
24163         if (!r) {
24164             return;
24165         }
24166         
24167         this.selections.remove(r);
24168         //.console.log('deselectRow - record id :' + r.id);
24169         if(!preventViewNotify){
24170         
24171             var proxy = new Roo.Element(
24172                 this.grid.getRowDom(index)
24173             );
24174             proxy.removeClass('bg-info info');
24175         }
24176         this.fireEvent("rowdeselect", this, index);
24177         this.fireEvent("selectionchange", this);
24178     },
24179
24180     // private
24181     restoreLast : function(){
24182         if(this._last){
24183             this.last = this._last;
24184         }
24185     },
24186
24187     // private
24188     acceptsNav : function(row, col, cm){
24189         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24190     },
24191
24192     // private
24193     onEditorKey : function(field, e){
24194         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24195         if(k == e.TAB){
24196             e.stopEvent();
24197             ed.completeEdit();
24198             if(e.shiftKey){
24199                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24200             }else{
24201                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24202             }
24203         }else if(k == e.ENTER && !e.ctrlKey){
24204             e.stopEvent();
24205             ed.completeEdit();
24206             if(e.shiftKey){
24207                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24208             }else{
24209                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24210             }
24211         }else if(k == e.ESC){
24212             ed.cancelEdit();
24213         }
24214         if(newCell){
24215             g.startEditing(newCell[0], newCell[1]);
24216         }
24217     }
24218 });
24219 /*
24220  * Based on:
24221  * Ext JS Library 1.1.1
24222  * Copyright(c) 2006-2007, Ext JS, LLC.
24223  *
24224  * Originally Released Under LGPL - original licence link has changed is not relivant.
24225  *
24226  * Fork - LGPL
24227  * <script type="text/javascript">
24228  */
24229  
24230 /**
24231  * @class Roo.bootstrap.PagingToolbar
24232  * @extends Roo.bootstrap.NavSimplebar
24233  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24234  * @constructor
24235  * Create a new PagingToolbar
24236  * @param {Object} config The config object
24237  * @param {Roo.data.Store} store
24238  */
24239 Roo.bootstrap.PagingToolbar = function(config)
24240 {
24241     // old args format still supported... - xtype is prefered..
24242         // created from xtype...
24243     
24244     this.ds = config.dataSource;
24245     
24246     if (config.store && !this.ds) {
24247         this.store= Roo.factory(config.store, Roo.data);
24248         this.ds = this.store;
24249         this.ds.xmodule = this.xmodule || false;
24250     }
24251     
24252     this.toolbarItems = [];
24253     if (config.items) {
24254         this.toolbarItems = config.items;
24255     }
24256     
24257     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24258     
24259     this.cursor = 0;
24260     
24261     if (this.ds) { 
24262         this.bind(this.ds);
24263     }
24264     
24265     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24266     
24267 };
24268
24269 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24270     /**
24271      * @cfg {Roo.data.Store} dataSource
24272      * The underlying data store providing the paged data
24273      */
24274     /**
24275      * @cfg {String/HTMLElement/Element} container
24276      * container The id or element that will contain the toolbar
24277      */
24278     /**
24279      * @cfg {Boolean} displayInfo
24280      * True to display the displayMsg (defaults to false)
24281      */
24282     /**
24283      * @cfg {Number} pageSize
24284      * The number of records to display per page (defaults to 20)
24285      */
24286     pageSize: 20,
24287     /**
24288      * @cfg {String} displayMsg
24289      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24290      */
24291     displayMsg : 'Displaying {0} - {1} of {2}',
24292     /**
24293      * @cfg {String} emptyMsg
24294      * The message to display when no records are found (defaults to "No data to display")
24295      */
24296     emptyMsg : 'No data to display',
24297     /**
24298      * Customizable piece of the default paging text (defaults to "Page")
24299      * @type String
24300      */
24301     beforePageText : "Page",
24302     /**
24303      * Customizable piece of the default paging text (defaults to "of %0")
24304      * @type String
24305      */
24306     afterPageText : "of {0}",
24307     /**
24308      * Customizable piece of the default paging text (defaults to "First Page")
24309      * @type String
24310      */
24311     firstText : "First Page",
24312     /**
24313      * Customizable piece of the default paging text (defaults to "Previous Page")
24314      * @type String
24315      */
24316     prevText : "Previous Page",
24317     /**
24318      * Customizable piece of the default paging text (defaults to "Next Page")
24319      * @type String
24320      */
24321     nextText : "Next Page",
24322     /**
24323      * Customizable piece of the default paging text (defaults to "Last Page")
24324      * @type String
24325      */
24326     lastText : "Last Page",
24327     /**
24328      * Customizable piece of the default paging text (defaults to "Refresh")
24329      * @type String
24330      */
24331     refreshText : "Refresh",
24332
24333     buttons : false,
24334     // private
24335     onRender : function(ct, position) 
24336     {
24337         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24338         this.navgroup.parentId = this.id;
24339         this.navgroup.onRender(this.el, null);
24340         // add the buttons to the navgroup
24341         
24342         if(this.displayInfo){
24343             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24344             this.displayEl = this.el.select('.x-paging-info', true).first();
24345 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24346 //            this.displayEl = navel.el.select('span',true).first();
24347         }
24348         
24349         var _this = this;
24350         
24351         if(this.buttons){
24352             Roo.each(_this.buttons, function(e){ // this might need to use render????
24353                Roo.factory(e).onRender(_this.el, null);
24354             });
24355         }
24356             
24357         Roo.each(_this.toolbarItems, function(e) {
24358             _this.navgroup.addItem(e);
24359         });
24360         
24361         
24362         this.first = this.navgroup.addItem({
24363             tooltip: this.firstText,
24364             cls: "prev",
24365             icon : 'fa fa-backward',
24366             disabled: true,
24367             preventDefault: true,
24368             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24369         });
24370         
24371         this.prev =  this.navgroup.addItem({
24372             tooltip: this.prevText,
24373             cls: "prev",
24374             icon : 'fa fa-step-backward',
24375             disabled: true,
24376             preventDefault: true,
24377             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24378         });
24379     //this.addSeparator();
24380         
24381         
24382         var field = this.navgroup.addItem( {
24383             tagtype : 'span',
24384             cls : 'x-paging-position',
24385             
24386             html : this.beforePageText  +
24387                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24388                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24389          } ); //?? escaped?
24390         
24391         this.field = field.el.select('input', true).first();
24392         this.field.on("keydown", this.onPagingKeydown, this);
24393         this.field.on("focus", function(){this.dom.select();});
24394     
24395     
24396         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24397         //this.field.setHeight(18);
24398         //this.addSeparator();
24399         this.next = this.navgroup.addItem({
24400             tooltip: this.nextText,
24401             cls: "next",
24402             html : ' <i class="fa fa-step-forward">',
24403             disabled: true,
24404             preventDefault: true,
24405             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24406         });
24407         this.last = this.navgroup.addItem({
24408             tooltip: this.lastText,
24409             icon : 'fa fa-forward',
24410             cls: "next",
24411             disabled: true,
24412             preventDefault: true,
24413             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24414         });
24415     //this.addSeparator();
24416         this.loading = this.navgroup.addItem({
24417             tooltip: this.refreshText,
24418             icon: 'fa fa-refresh',
24419             preventDefault: true,
24420             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24421         });
24422         
24423     },
24424
24425     // private
24426     updateInfo : function(){
24427         if(this.displayEl){
24428             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24429             var msg = count == 0 ?
24430                 this.emptyMsg :
24431                 String.format(
24432                     this.displayMsg,
24433                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24434                 );
24435             this.displayEl.update(msg);
24436         }
24437     },
24438
24439     // private
24440     onLoad : function(ds, r, o)
24441     {
24442         this.cursor = o.params ? o.params.start : 0;
24443         
24444         Roo.log('loading for paging');
24445         
24446         Roo.log(o.params);
24447         
24448         var d = this.getPageData(),
24449             ap = d.activePage,
24450             ps = d.pages;
24451         
24452         
24453         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24454         this.field.dom.value = ap;
24455         this.first.setDisabled(ap == 1);
24456         this.prev.setDisabled(ap == 1);
24457         this.next.setDisabled(ap == ps);
24458         this.last.setDisabled(ap == ps);
24459         this.loading.enable();
24460         this.updateInfo();
24461     },
24462
24463     // private
24464     getPageData : function(){
24465         var total = this.ds.getTotalCount();
24466         return {
24467             total : total,
24468             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24469             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24470         };
24471     },
24472
24473     // private
24474     onLoadError : function(){
24475         this.loading.enable();
24476     },
24477
24478     // private
24479     onPagingKeydown : function(e){
24480         var k = e.getKey();
24481         var d = this.getPageData();
24482         if(k == e.RETURN){
24483             var v = this.field.dom.value, pageNum;
24484             if(!v || isNaN(pageNum = parseInt(v, 10))){
24485                 this.field.dom.value = d.activePage;
24486                 return;
24487             }
24488             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24489             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24490             e.stopEvent();
24491         }
24492         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))
24493         {
24494           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24495           this.field.dom.value = pageNum;
24496           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24497           e.stopEvent();
24498         }
24499         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24500         {
24501           var v = this.field.dom.value, pageNum; 
24502           var increment = (e.shiftKey) ? 10 : 1;
24503           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24504                 increment *= -1;
24505           }
24506           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24507             this.field.dom.value = d.activePage;
24508             return;
24509           }
24510           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24511           {
24512             this.field.dom.value = parseInt(v, 10) + increment;
24513             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24514             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24515           }
24516           e.stopEvent();
24517         }
24518     },
24519
24520     // private
24521     beforeLoad : function(){
24522         if(this.loading){
24523             this.loading.disable();
24524         }
24525     },
24526
24527     // private
24528     onClick : function(which){
24529         
24530         var ds = this.ds;
24531         if (!ds) {
24532             return;
24533         }
24534         
24535         switch(which){
24536             case "first":
24537                 ds.load({params:{start: 0, limit: this.pageSize}});
24538             break;
24539             case "prev":
24540                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24541             break;
24542             case "next":
24543                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24544             break;
24545             case "last":
24546                 var total = ds.getTotalCount();
24547                 var extra = total % this.pageSize;
24548                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24549                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24550             break;
24551             case "refresh":
24552                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24553             break;
24554         }
24555     },
24556
24557     /**
24558      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24559      * @param {Roo.data.Store} store The data store to unbind
24560      */
24561     unbind : function(ds){
24562         ds.un("beforeload", this.beforeLoad, this);
24563         ds.un("load", this.onLoad, this);
24564         ds.un("loadexception", this.onLoadError, this);
24565         ds.un("remove", this.updateInfo, this);
24566         ds.un("add", this.updateInfo, this);
24567         this.ds = undefined;
24568     },
24569
24570     /**
24571      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24572      * @param {Roo.data.Store} store The data store to bind
24573      */
24574     bind : function(ds){
24575         ds.on("beforeload", this.beforeLoad, this);
24576         ds.on("load", this.onLoad, this);
24577         ds.on("loadexception", this.onLoadError, this);
24578         ds.on("remove", this.updateInfo, this);
24579         ds.on("add", this.updateInfo, this);
24580         this.ds = ds;
24581     }
24582 });/*
24583  * - LGPL
24584  *
24585  * element
24586  * 
24587  */
24588
24589 /**
24590  * @class Roo.bootstrap.MessageBar
24591  * @extends Roo.bootstrap.Component
24592  * Bootstrap MessageBar class
24593  * @cfg {String} html contents of the MessageBar
24594  * @cfg {String} weight (info | success | warning | danger) default info
24595  * @cfg {String} beforeClass insert the bar before the given class
24596  * @cfg {Boolean} closable (true | false) default false
24597  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24598  * 
24599  * @constructor
24600  * Create a new Element
24601  * @param {Object} config The config object
24602  */
24603
24604 Roo.bootstrap.MessageBar = function(config){
24605     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24606 };
24607
24608 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24609     
24610     html: '',
24611     weight: 'info',
24612     closable: false,
24613     fixed: false,
24614     beforeClass: 'bootstrap-sticky-wrap',
24615     
24616     getAutoCreate : function(){
24617         
24618         var cfg = {
24619             tag: 'div',
24620             cls: 'alert alert-dismissable alert-' + this.weight,
24621             cn: [
24622                 {
24623                     tag: 'span',
24624                     cls: 'message',
24625                     html: this.html || ''
24626                 }
24627             ]
24628         };
24629         
24630         if(this.fixed){
24631             cfg.cls += ' alert-messages-fixed';
24632         }
24633         
24634         if(this.closable){
24635             cfg.cn.push({
24636                 tag: 'button',
24637                 cls: 'close',
24638                 html: 'x'
24639             });
24640         }
24641         
24642         return cfg;
24643     },
24644     
24645     onRender : function(ct, position)
24646     {
24647         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24648         
24649         if(!this.el){
24650             var cfg = Roo.apply({},  this.getAutoCreate());
24651             cfg.id = Roo.id();
24652             
24653             if (this.cls) {
24654                 cfg.cls += ' ' + this.cls;
24655             }
24656             if (this.style) {
24657                 cfg.style = this.style;
24658             }
24659             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24660             
24661             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24662         }
24663         
24664         this.el.select('>button.close').on('click', this.hide, this);
24665         
24666     },
24667     
24668     show : function()
24669     {
24670         if (!this.rendered) {
24671             this.render();
24672         }
24673         
24674         this.el.show();
24675         
24676         this.fireEvent('show', this);
24677         
24678     },
24679     
24680     hide : function()
24681     {
24682         if (!this.rendered) {
24683             this.render();
24684         }
24685         
24686         this.el.hide();
24687         
24688         this.fireEvent('hide', this);
24689     },
24690     
24691     update : function()
24692     {
24693 //        var e = this.el.dom.firstChild;
24694 //        
24695 //        if(this.closable){
24696 //            e = e.nextSibling;
24697 //        }
24698 //        
24699 //        e.data = this.html || '';
24700
24701         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24702     }
24703    
24704 });
24705
24706  
24707
24708      /*
24709  * - LGPL
24710  *
24711  * Graph
24712  * 
24713  */
24714
24715
24716 /**
24717  * @class Roo.bootstrap.Graph
24718  * @extends Roo.bootstrap.Component
24719  * Bootstrap Graph class
24720 > Prameters
24721  -sm {number} sm 4
24722  -md {number} md 5
24723  @cfg {String} graphtype  bar | vbar | pie
24724  @cfg {number} g_x coodinator | centre x (pie)
24725  @cfg {number} g_y coodinator | centre y (pie)
24726  @cfg {number} g_r radius (pie)
24727  @cfg {number} g_height height of the chart (respected by all elements in the set)
24728  @cfg {number} g_width width of the chart (respected by all elements in the set)
24729  @cfg {Object} title The title of the chart
24730     
24731  -{Array}  values
24732  -opts (object) options for the chart 
24733      o {
24734      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24735      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24736      o vgutter (number)
24737      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.
24738      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24739      o to
24740      o stretch (boolean)
24741      o }
24742  -opts (object) options for the pie
24743      o{
24744      o cut
24745      o startAngle (number)
24746      o endAngle (number)
24747      } 
24748  *
24749  * @constructor
24750  * Create a new Input
24751  * @param {Object} config The config object
24752  */
24753
24754 Roo.bootstrap.Graph = function(config){
24755     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24756     
24757     this.addEvents({
24758         // img events
24759         /**
24760          * @event click
24761          * The img click event for the img.
24762          * @param {Roo.EventObject} e
24763          */
24764         "click" : true
24765     });
24766 };
24767
24768 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24769     
24770     sm: 4,
24771     md: 5,
24772     graphtype: 'bar',
24773     g_height: 250,
24774     g_width: 400,
24775     g_x: 50,
24776     g_y: 50,
24777     g_r: 30,
24778     opts:{
24779         //g_colors: this.colors,
24780         g_type: 'soft',
24781         g_gutter: '20%'
24782
24783     },
24784     title : false,
24785
24786     getAutoCreate : function(){
24787         
24788         var cfg = {
24789             tag: 'div',
24790             html : null
24791         };
24792         
24793         
24794         return  cfg;
24795     },
24796
24797     onRender : function(ct,position){
24798         
24799         
24800         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24801         
24802         if (typeof(Raphael) == 'undefined') {
24803             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24804             return;
24805         }
24806         
24807         this.raphael = Raphael(this.el.dom);
24808         
24809                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24810                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24811                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24812                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24813                 /*
24814                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24815                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24816                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24817                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24818                 
24819                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24820                 r.barchart(330, 10, 300, 220, data1);
24821                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24822                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24823                 */
24824                 
24825                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24826                 // r.barchart(30, 30, 560, 250,  xdata, {
24827                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24828                 //     axis : "0 0 1 1",
24829                 //     axisxlabels :  xdata
24830                 //     //yvalues : cols,
24831                    
24832                 // });
24833 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24834 //        
24835 //        this.load(null,xdata,{
24836 //                axis : "0 0 1 1",
24837 //                axisxlabels :  xdata
24838 //                });
24839
24840     },
24841
24842     load : function(graphtype,xdata,opts)
24843     {
24844         this.raphael.clear();
24845         if(!graphtype) {
24846             graphtype = this.graphtype;
24847         }
24848         if(!opts){
24849             opts = this.opts;
24850         }
24851         var r = this.raphael,
24852             fin = function () {
24853                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24854             },
24855             fout = function () {
24856                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24857             },
24858             pfin = function() {
24859                 this.sector.stop();
24860                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24861
24862                 if (this.label) {
24863                     this.label[0].stop();
24864                     this.label[0].attr({ r: 7.5 });
24865                     this.label[1].attr({ "font-weight": 800 });
24866                 }
24867             },
24868             pfout = function() {
24869                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24870
24871                 if (this.label) {
24872                     this.label[0].animate({ r: 5 }, 500, "bounce");
24873                     this.label[1].attr({ "font-weight": 400 });
24874                 }
24875             };
24876
24877         switch(graphtype){
24878             case 'bar':
24879                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24880                 break;
24881             case 'hbar':
24882                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24883                 break;
24884             case 'pie':
24885 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24886 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24887 //            
24888                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24889                 
24890                 break;
24891
24892         }
24893         
24894         if(this.title){
24895             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24896         }
24897         
24898     },
24899     
24900     setTitle: function(o)
24901     {
24902         this.title = o;
24903     },
24904     
24905     initEvents: function() {
24906         
24907         if(!this.href){
24908             this.el.on('click', this.onClick, this);
24909         }
24910     },
24911     
24912     onClick : function(e)
24913     {
24914         Roo.log('img onclick');
24915         this.fireEvent('click', this, e);
24916     }
24917    
24918 });
24919
24920  
24921 /*
24922  * - LGPL
24923  *
24924  * numberBox
24925  * 
24926  */
24927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24928
24929 /**
24930  * @class Roo.bootstrap.dash.NumberBox
24931  * @extends Roo.bootstrap.Component
24932  * Bootstrap NumberBox class
24933  * @cfg {String} headline Box headline
24934  * @cfg {String} content Box content
24935  * @cfg {String} icon Box icon
24936  * @cfg {String} footer Footer text
24937  * @cfg {String} fhref Footer href
24938  * 
24939  * @constructor
24940  * Create a new NumberBox
24941  * @param {Object} config The config object
24942  */
24943
24944
24945 Roo.bootstrap.dash.NumberBox = function(config){
24946     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24947     
24948 };
24949
24950 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24951     
24952     headline : '',
24953     content : '',
24954     icon : '',
24955     footer : '',
24956     fhref : '',
24957     ficon : '',
24958     
24959     getAutoCreate : function(){
24960         
24961         var cfg = {
24962             tag : 'div',
24963             cls : 'small-box ',
24964             cn : [
24965                 {
24966                     tag : 'div',
24967                     cls : 'inner',
24968                     cn :[
24969                         {
24970                             tag : 'h3',
24971                             cls : 'roo-headline',
24972                             html : this.headline
24973                         },
24974                         {
24975                             tag : 'p',
24976                             cls : 'roo-content',
24977                             html : this.content
24978                         }
24979                     ]
24980                 }
24981             ]
24982         };
24983         
24984         if(this.icon){
24985             cfg.cn.push({
24986                 tag : 'div',
24987                 cls : 'icon',
24988                 cn :[
24989                     {
24990                         tag : 'i',
24991                         cls : 'ion ' + this.icon
24992                     }
24993                 ]
24994             });
24995         }
24996         
24997         if(this.footer){
24998             var footer = {
24999                 tag : 'a',
25000                 cls : 'small-box-footer',
25001                 href : this.fhref || '#',
25002                 html : this.footer
25003             };
25004             
25005             cfg.cn.push(footer);
25006             
25007         }
25008         
25009         return  cfg;
25010     },
25011
25012     onRender : function(ct,position){
25013         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25014
25015
25016        
25017                 
25018     },
25019
25020     setHeadline: function (value)
25021     {
25022         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25023     },
25024     
25025     setFooter: function (value, href)
25026     {
25027         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25028         
25029         if(href){
25030             this.el.select('a.small-box-footer',true).first().attr('href', href);
25031         }
25032         
25033     },
25034
25035     setContent: function (value)
25036     {
25037         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25038     },
25039
25040     initEvents: function() 
25041     {   
25042         
25043     }
25044     
25045 });
25046
25047  
25048 /*
25049  * - LGPL
25050  *
25051  * TabBox
25052  * 
25053  */
25054 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25055
25056 /**
25057  * @class Roo.bootstrap.dash.TabBox
25058  * @extends Roo.bootstrap.Component
25059  * Bootstrap TabBox class
25060  * @cfg {String} title Title of the TabBox
25061  * @cfg {String} icon Icon of the TabBox
25062  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25063  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25064  * 
25065  * @constructor
25066  * Create a new TabBox
25067  * @param {Object} config The config object
25068  */
25069
25070
25071 Roo.bootstrap.dash.TabBox = function(config){
25072     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25073     this.addEvents({
25074         // raw events
25075         /**
25076          * @event addpane
25077          * When a pane is added
25078          * @param {Roo.bootstrap.dash.TabPane} pane
25079          */
25080         "addpane" : true,
25081         /**
25082          * @event activatepane
25083          * When a pane is activated
25084          * @param {Roo.bootstrap.dash.TabPane} pane
25085          */
25086         "activatepane" : true
25087         
25088          
25089     });
25090     
25091     this.panes = [];
25092 };
25093
25094 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25095
25096     title : '',
25097     icon : false,
25098     showtabs : true,
25099     tabScrollable : false,
25100     
25101     getChildContainer : function()
25102     {
25103         return this.el.select('.tab-content', true).first();
25104     },
25105     
25106     getAutoCreate : function(){
25107         
25108         var header = {
25109             tag: 'li',
25110             cls: 'pull-left header',
25111             html: this.title,
25112             cn : []
25113         };
25114         
25115         if(this.icon){
25116             header.cn.push({
25117                 tag: 'i',
25118                 cls: 'fa ' + this.icon
25119             });
25120         }
25121         
25122         var h = {
25123             tag: 'ul',
25124             cls: 'nav nav-tabs pull-right',
25125             cn: [
25126                 header
25127             ]
25128         };
25129         
25130         if(this.tabScrollable){
25131             h = {
25132                 tag: 'div',
25133                 cls: 'tab-header',
25134                 cn: [
25135                     {
25136                         tag: 'ul',
25137                         cls: 'nav nav-tabs pull-right',
25138                         cn: [
25139                             header
25140                         ]
25141                     }
25142                 ]
25143             };
25144         }
25145         
25146         var cfg = {
25147             tag: 'div',
25148             cls: 'nav-tabs-custom',
25149             cn: [
25150                 h,
25151                 {
25152                     tag: 'div',
25153                     cls: 'tab-content no-padding',
25154                     cn: []
25155                 }
25156             ]
25157         };
25158
25159         return  cfg;
25160     },
25161     initEvents : function()
25162     {
25163         //Roo.log('add add pane handler');
25164         this.on('addpane', this.onAddPane, this);
25165     },
25166      /**
25167      * Updates the box title
25168      * @param {String} html to set the title to.
25169      */
25170     setTitle : function(value)
25171     {
25172         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25173     },
25174     onAddPane : function(pane)
25175     {
25176         this.panes.push(pane);
25177         //Roo.log('addpane');
25178         //Roo.log(pane);
25179         // tabs are rendere left to right..
25180         if(!this.showtabs){
25181             return;
25182         }
25183         
25184         var ctr = this.el.select('.nav-tabs', true).first();
25185          
25186          
25187         var existing = ctr.select('.nav-tab',true);
25188         var qty = existing.getCount();;
25189         
25190         
25191         var tab = ctr.createChild({
25192             tag : 'li',
25193             cls : 'nav-tab' + (qty ? '' : ' active'),
25194             cn : [
25195                 {
25196                     tag : 'a',
25197                     href:'#',
25198                     html : pane.title
25199                 }
25200             ]
25201         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25202         pane.tab = tab;
25203         
25204         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25205         if (!qty) {
25206             pane.el.addClass('active');
25207         }
25208         
25209                 
25210     },
25211     onTabClick : function(ev,un,ob,pane)
25212     {
25213         //Roo.log('tab - prev default');
25214         ev.preventDefault();
25215         
25216         
25217         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25218         pane.tab.addClass('active');
25219         //Roo.log(pane.title);
25220         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25221         // technically we should have a deactivate event.. but maybe add later.
25222         // and it should not de-activate the selected tab...
25223         this.fireEvent('activatepane', pane);
25224         pane.el.addClass('active');
25225         pane.fireEvent('activate');
25226         
25227         
25228     },
25229     
25230     getActivePane : function()
25231     {
25232         var r = false;
25233         Roo.each(this.panes, function(p) {
25234             if(p.el.hasClass('active')){
25235                 r = p;
25236                 return false;
25237             }
25238             
25239             return;
25240         });
25241         
25242         return r;
25243     }
25244     
25245     
25246 });
25247
25248  
25249 /*
25250  * - LGPL
25251  *
25252  * Tab pane
25253  * 
25254  */
25255 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25256 /**
25257  * @class Roo.bootstrap.TabPane
25258  * @extends Roo.bootstrap.Component
25259  * Bootstrap TabPane class
25260  * @cfg {Boolean} active (false | true) Default false
25261  * @cfg {String} title title of panel
25262
25263  * 
25264  * @constructor
25265  * Create a new TabPane
25266  * @param {Object} config The config object
25267  */
25268
25269 Roo.bootstrap.dash.TabPane = function(config){
25270     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25271     
25272     this.addEvents({
25273         // raw events
25274         /**
25275          * @event activate
25276          * When a pane is activated
25277          * @param {Roo.bootstrap.dash.TabPane} pane
25278          */
25279         "activate" : true
25280          
25281     });
25282 };
25283
25284 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25285     
25286     active : false,
25287     title : '',
25288     
25289     // the tabBox that this is attached to.
25290     tab : false,
25291      
25292     getAutoCreate : function() 
25293     {
25294         var cfg = {
25295             tag: 'div',
25296             cls: 'tab-pane'
25297         };
25298         
25299         if(this.active){
25300             cfg.cls += ' active';
25301         }
25302         
25303         return cfg;
25304     },
25305     initEvents  : function()
25306     {
25307         //Roo.log('trigger add pane handler');
25308         this.parent().fireEvent('addpane', this)
25309     },
25310     
25311      /**
25312      * Updates the tab title 
25313      * @param {String} html to set the title to.
25314      */
25315     setTitle: function(str)
25316     {
25317         if (!this.tab) {
25318             return;
25319         }
25320         this.title = str;
25321         this.tab.select('a', true).first().dom.innerHTML = str;
25322         
25323     }
25324     
25325     
25326     
25327 });
25328
25329  
25330
25331
25332  /*
25333  * - LGPL
25334  *
25335  * menu
25336  * 
25337  */
25338 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25339
25340 /**
25341  * @class Roo.bootstrap.menu.Menu
25342  * @extends Roo.bootstrap.Component
25343  * Bootstrap Menu class - container for Menu
25344  * @cfg {String} html Text of the menu
25345  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25346  * @cfg {String} icon Font awesome icon
25347  * @cfg {String} pos Menu align to (top | bottom) default bottom
25348  * 
25349  * 
25350  * @constructor
25351  * Create a new Menu
25352  * @param {Object} config The config object
25353  */
25354
25355
25356 Roo.bootstrap.menu.Menu = function(config){
25357     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25358     
25359     this.addEvents({
25360         /**
25361          * @event beforeshow
25362          * Fires before this menu is displayed
25363          * @param {Roo.bootstrap.menu.Menu} this
25364          */
25365         beforeshow : true,
25366         /**
25367          * @event beforehide
25368          * Fires before this menu is hidden
25369          * @param {Roo.bootstrap.menu.Menu} this
25370          */
25371         beforehide : true,
25372         /**
25373          * @event show
25374          * Fires after this menu is displayed
25375          * @param {Roo.bootstrap.menu.Menu} this
25376          */
25377         show : true,
25378         /**
25379          * @event hide
25380          * Fires after this menu is hidden
25381          * @param {Roo.bootstrap.menu.Menu} this
25382          */
25383         hide : true,
25384         /**
25385          * @event click
25386          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25387          * @param {Roo.bootstrap.menu.Menu} this
25388          * @param {Roo.EventObject} e
25389          */
25390         click : true
25391     });
25392     
25393 };
25394
25395 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25396     
25397     submenu : false,
25398     html : '',
25399     weight : 'default',
25400     icon : false,
25401     pos : 'bottom',
25402     
25403     
25404     getChildContainer : function() {
25405         if(this.isSubMenu){
25406             return this.el;
25407         }
25408         
25409         return this.el.select('ul.dropdown-menu', true).first();  
25410     },
25411     
25412     getAutoCreate : function()
25413     {
25414         var text = [
25415             {
25416                 tag : 'span',
25417                 cls : 'roo-menu-text',
25418                 html : this.html
25419             }
25420         ];
25421         
25422         if(this.icon){
25423             text.unshift({
25424                 tag : 'i',
25425                 cls : 'fa ' + this.icon
25426             })
25427         }
25428         
25429         
25430         var cfg = {
25431             tag : 'div',
25432             cls : 'btn-group',
25433             cn : [
25434                 {
25435                     tag : 'button',
25436                     cls : 'dropdown-button btn btn-' + this.weight,
25437                     cn : text
25438                 },
25439                 {
25440                     tag : 'button',
25441                     cls : 'dropdown-toggle btn btn-' + this.weight,
25442                     cn : [
25443                         {
25444                             tag : 'span',
25445                             cls : 'caret'
25446                         }
25447                     ]
25448                 },
25449                 {
25450                     tag : 'ul',
25451                     cls : 'dropdown-menu'
25452                 }
25453             ]
25454             
25455         };
25456         
25457         if(this.pos == 'top'){
25458             cfg.cls += ' dropup';
25459         }
25460         
25461         if(this.isSubMenu){
25462             cfg = {
25463                 tag : 'ul',
25464                 cls : 'dropdown-menu'
25465             }
25466         }
25467         
25468         return cfg;
25469     },
25470     
25471     onRender : function(ct, position)
25472     {
25473         this.isSubMenu = ct.hasClass('dropdown-submenu');
25474         
25475         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25476     },
25477     
25478     initEvents : function() 
25479     {
25480         if(this.isSubMenu){
25481             return;
25482         }
25483         
25484         this.hidden = true;
25485         
25486         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25487         this.triggerEl.on('click', this.onTriggerPress, this);
25488         
25489         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25490         this.buttonEl.on('click', this.onClick, this);
25491         
25492     },
25493     
25494     list : function()
25495     {
25496         if(this.isSubMenu){
25497             return this.el;
25498         }
25499         
25500         return this.el.select('ul.dropdown-menu', true).first();
25501     },
25502     
25503     onClick : function(e)
25504     {
25505         this.fireEvent("click", this, e);
25506     },
25507     
25508     onTriggerPress  : function(e)
25509     {   
25510         if (this.isVisible()) {
25511             this.hide();
25512         } else {
25513             this.show();
25514         }
25515     },
25516     
25517     isVisible : function(){
25518         return !this.hidden;
25519     },
25520     
25521     show : function()
25522     {
25523         this.fireEvent("beforeshow", this);
25524         
25525         this.hidden = false;
25526         this.el.addClass('open');
25527         
25528         Roo.get(document).on("mouseup", this.onMouseUp, this);
25529         
25530         this.fireEvent("show", this);
25531         
25532         
25533     },
25534     
25535     hide : function()
25536     {
25537         this.fireEvent("beforehide", this);
25538         
25539         this.hidden = true;
25540         this.el.removeClass('open');
25541         
25542         Roo.get(document).un("mouseup", this.onMouseUp);
25543         
25544         this.fireEvent("hide", this);
25545     },
25546     
25547     onMouseUp : function()
25548     {
25549         this.hide();
25550     }
25551     
25552 });
25553
25554  
25555  /*
25556  * - LGPL
25557  *
25558  * menu item
25559  * 
25560  */
25561 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25562
25563 /**
25564  * @class Roo.bootstrap.menu.Item
25565  * @extends Roo.bootstrap.Component
25566  * Bootstrap MenuItem class
25567  * @cfg {Boolean} submenu (true | false) default false
25568  * @cfg {String} html text of the item
25569  * @cfg {String} href the link
25570  * @cfg {Boolean} disable (true | false) default false
25571  * @cfg {Boolean} preventDefault (true | false) default true
25572  * @cfg {String} icon Font awesome icon
25573  * @cfg {String} pos Submenu align to (left | right) default right 
25574  * 
25575  * 
25576  * @constructor
25577  * Create a new Item
25578  * @param {Object} config The config object
25579  */
25580
25581
25582 Roo.bootstrap.menu.Item = function(config){
25583     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25584     this.addEvents({
25585         /**
25586          * @event mouseover
25587          * Fires when the mouse is hovering over this menu
25588          * @param {Roo.bootstrap.menu.Item} this
25589          * @param {Roo.EventObject} e
25590          */
25591         mouseover : true,
25592         /**
25593          * @event mouseout
25594          * Fires when the mouse exits this menu
25595          * @param {Roo.bootstrap.menu.Item} this
25596          * @param {Roo.EventObject} e
25597          */
25598         mouseout : true,
25599         // raw events
25600         /**
25601          * @event click
25602          * The raw click event for the entire grid.
25603          * @param {Roo.EventObject} e
25604          */
25605         click : true
25606     });
25607 };
25608
25609 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25610     
25611     submenu : false,
25612     href : '',
25613     html : '',
25614     preventDefault: true,
25615     disable : false,
25616     icon : false,
25617     pos : 'right',
25618     
25619     getAutoCreate : function()
25620     {
25621         var text = [
25622             {
25623                 tag : 'span',
25624                 cls : 'roo-menu-item-text',
25625                 html : this.html
25626             }
25627         ];
25628         
25629         if(this.icon){
25630             text.unshift({
25631                 tag : 'i',
25632                 cls : 'fa ' + this.icon
25633             })
25634         }
25635         
25636         var cfg = {
25637             tag : 'li',
25638             cn : [
25639                 {
25640                     tag : 'a',
25641                     href : this.href || '#',
25642                     cn : text
25643                 }
25644             ]
25645         };
25646         
25647         if(this.disable){
25648             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25649         }
25650         
25651         if(this.submenu){
25652             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25653             
25654             if(this.pos == 'left'){
25655                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25656             }
25657         }
25658         
25659         return cfg;
25660     },
25661     
25662     initEvents : function() 
25663     {
25664         this.el.on('mouseover', this.onMouseOver, this);
25665         this.el.on('mouseout', this.onMouseOut, this);
25666         
25667         this.el.select('a', true).first().on('click', this.onClick, this);
25668         
25669     },
25670     
25671     onClick : function(e)
25672     {
25673         if(this.preventDefault){
25674             e.preventDefault();
25675         }
25676         
25677         this.fireEvent("click", this, e);
25678     },
25679     
25680     onMouseOver : function(e)
25681     {
25682         if(this.submenu && this.pos == 'left'){
25683             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25684         }
25685         
25686         this.fireEvent("mouseover", this, e);
25687     },
25688     
25689     onMouseOut : function(e)
25690     {
25691         this.fireEvent("mouseout", this, e);
25692     }
25693 });
25694
25695  
25696
25697  /*
25698  * - LGPL
25699  *
25700  * menu separator
25701  * 
25702  */
25703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25704
25705 /**
25706  * @class Roo.bootstrap.menu.Separator
25707  * @extends Roo.bootstrap.Component
25708  * Bootstrap Separator class
25709  * 
25710  * @constructor
25711  * Create a new Separator
25712  * @param {Object} config The config object
25713  */
25714
25715
25716 Roo.bootstrap.menu.Separator = function(config){
25717     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25718 };
25719
25720 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25721     
25722     getAutoCreate : function(){
25723         var cfg = {
25724             tag : 'li',
25725             cls: 'divider'
25726         };
25727         
25728         return cfg;
25729     }
25730    
25731 });
25732
25733  
25734
25735  /*
25736  * - LGPL
25737  *
25738  * Tooltip
25739  * 
25740  */
25741
25742 /**
25743  * @class Roo.bootstrap.Tooltip
25744  * Bootstrap Tooltip class
25745  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25746  * to determine which dom element triggers the tooltip.
25747  * 
25748  * It needs to add support for additional attributes like tooltip-position
25749  * 
25750  * @constructor
25751  * Create a new Toolti
25752  * @param {Object} config The config object
25753  */
25754
25755 Roo.bootstrap.Tooltip = function(config){
25756     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25757     
25758     this.alignment = Roo.bootstrap.Tooltip.alignment;
25759     
25760     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25761         this.alignment = config.alignment;
25762     }
25763     
25764 };
25765
25766 Roo.apply(Roo.bootstrap.Tooltip, {
25767     /**
25768      * @function init initialize tooltip monitoring.
25769      * @static
25770      */
25771     currentEl : false,
25772     currentTip : false,
25773     currentRegion : false,
25774     
25775     //  init : delay?
25776     
25777     init : function()
25778     {
25779         Roo.get(document).on('mouseover', this.enter ,this);
25780         Roo.get(document).on('mouseout', this.leave, this);
25781          
25782         
25783         this.currentTip = new Roo.bootstrap.Tooltip();
25784     },
25785     
25786     enter : function(ev)
25787     {
25788         var dom = ev.getTarget();
25789         
25790         //Roo.log(['enter',dom]);
25791         var el = Roo.fly(dom);
25792         if (this.currentEl) {
25793             //Roo.log(dom);
25794             //Roo.log(this.currentEl);
25795             //Roo.log(this.currentEl.contains(dom));
25796             if (this.currentEl == el) {
25797                 return;
25798             }
25799             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25800                 return;
25801             }
25802
25803         }
25804         
25805         if (this.currentTip.el) {
25806             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25807         }    
25808         //Roo.log(ev);
25809         
25810         if(!el || el.dom == document){
25811             return;
25812         }
25813         
25814         var bindEl = el;
25815         
25816         // you can not look for children, as if el is the body.. then everythign is the child..
25817         if (!el.attr('tooltip')) { //
25818             if (!el.select("[tooltip]").elements.length) {
25819                 return;
25820             }
25821             // is the mouse over this child...?
25822             bindEl = el.select("[tooltip]").first();
25823             var xy = ev.getXY();
25824             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25825                 //Roo.log("not in region.");
25826                 return;
25827             }
25828             //Roo.log("child element over..");
25829             
25830         }
25831         this.currentEl = bindEl;
25832         this.currentTip.bind(bindEl);
25833         this.currentRegion = Roo.lib.Region.getRegion(dom);
25834         this.currentTip.enter();
25835         
25836     },
25837     leave : function(ev)
25838     {
25839         var dom = ev.getTarget();
25840         //Roo.log(['leave',dom]);
25841         if (!this.currentEl) {
25842             return;
25843         }
25844         
25845         
25846         if (dom != this.currentEl.dom) {
25847             return;
25848         }
25849         var xy = ev.getXY();
25850         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25851             return;
25852         }
25853         // only activate leave if mouse cursor is outside... bounding box..
25854         
25855         
25856         
25857         
25858         if (this.currentTip) {
25859             this.currentTip.leave();
25860         }
25861         //Roo.log('clear currentEl');
25862         this.currentEl = false;
25863         
25864         
25865     },
25866     alignment : {
25867         'left' : ['r-l', [-2,0], 'right'],
25868         'right' : ['l-r', [2,0], 'left'],
25869         'bottom' : ['t-b', [0,2], 'top'],
25870         'top' : [ 'b-t', [0,-2], 'bottom']
25871     }
25872     
25873 });
25874
25875
25876 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25877     
25878     
25879     bindEl : false,
25880     
25881     delay : null, // can be { show : 300 , hide: 500}
25882     
25883     timeout : null,
25884     
25885     hoverState : null, //???
25886     
25887     placement : 'bottom', 
25888     
25889     alignment : false,
25890     
25891     getAutoCreate : function(){
25892     
25893         var cfg = {
25894            cls : 'tooltip',
25895            role : 'tooltip',
25896            cn : [
25897                 {
25898                     cls : 'tooltip-arrow'
25899                 },
25900                 {
25901                     cls : 'tooltip-inner'
25902                 }
25903            ]
25904         };
25905         
25906         return cfg;
25907     },
25908     bind : function(el)
25909     {
25910         this.bindEl = el;
25911     },
25912       
25913     
25914     enter : function () {
25915        
25916         if (this.timeout != null) {
25917             clearTimeout(this.timeout);
25918         }
25919         
25920         this.hoverState = 'in';
25921          //Roo.log("enter - show");
25922         if (!this.delay || !this.delay.show) {
25923             this.show();
25924             return;
25925         }
25926         var _t = this;
25927         this.timeout = setTimeout(function () {
25928             if (_t.hoverState == 'in') {
25929                 _t.show();
25930             }
25931         }, this.delay.show);
25932     },
25933     leave : function()
25934     {
25935         clearTimeout(this.timeout);
25936     
25937         this.hoverState = 'out';
25938          if (!this.delay || !this.delay.hide) {
25939             this.hide();
25940             return;
25941         }
25942        
25943         var _t = this;
25944         this.timeout = setTimeout(function () {
25945             //Roo.log("leave - timeout");
25946             
25947             if (_t.hoverState == 'out') {
25948                 _t.hide();
25949                 Roo.bootstrap.Tooltip.currentEl = false;
25950             }
25951         }, delay);
25952     },
25953     
25954     show : function (msg)
25955     {
25956         if (!this.el) {
25957             this.render(document.body);
25958         }
25959         // set content.
25960         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25961         
25962         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25963         
25964         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25965         
25966         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25967         
25968         var placement = typeof this.placement == 'function' ?
25969             this.placement.call(this, this.el, on_el) :
25970             this.placement;
25971             
25972         var autoToken = /\s?auto?\s?/i;
25973         var autoPlace = autoToken.test(placement);
25974         if (autoPlace) {
25975             placement = placement.replace(autoToken, '') || 'top';
25976         }
25977         
25978         //this.el.detach()
25979         //this.el.setXY([0,0]);
25980         this.el.show();
25981         //this.el.dom.style.display='block';
25982         
25983         //this.el.appendTo(on_el);
25984         
25985         var p = this.getPosition();
25986         var box = this.el.getBox();
25987         
25988         if (autoPlace) {
25989             // fixme..
25990         }
25991         
25992         var align = this.alignment[placement];
25993         
25994         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25995         
25996         if(placement == 'top' || placement == 'bottom'){
25997             if(xy[0] < 0){
25998                 placement = 'right';
25999             }
26000             
26001             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26002                 placement = 'left';
26003             }
26004             
26005             var scroll = Roo.select('body', true).first().getScroll();
26006             
26007             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26008                 placement = 'top';
26009             }
26010             
26011         }
26012         
26013         this.el.alignTo(this.bindEl, align[0],align[1]);
26014         //var arrow = this.el.select('.arrow',true).first();
26015         //arrow.set(align[2], 
26016         
26017         this.el.addClass(placement);
26018         
26019         this.el.addClass('in fade');
26020         
26021         this.hoverState = null;
26022         
26023         if (this.el.hasClass('fade')) {
26024             // fade it?
26025         }
26026         
26027     },
26028     hide : function()
26029     {
26030          
26031         if (!this.el) {
26032             return;
26033         }
26034         //this.el.setXY([0,0]);
26035         this.el.removeClass('in');
26036         //this.el.hide();
26037         
26038     }
26039     
26040 });
26041  
26042
26043  /*
26044  * - LGPL
26045  *
26046  * Location Picker
26047  * 
26048  */
26049
26050 /**
26051  * @class Roo.bootstrap.LocationPicker
26052  * @extends Roo.bootstrap.Component
26053  * Bootstrap LocationPicker class
26054  * @cfg {Number} latitude Position when init default 0
26055  * @cfg {Number} longitude Position when init default 0
26056  * @cfg {Number} zoom default 15
26057  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26058  * @cfg {Boolean} mapTypeControl default false
26059  * @cfg {Boolean} disableDoubleClickZoom default false
26060  * @cfg {Boolean} scrollwheel default true
26061  * @cfg {Boolean} streetViewControl default false
26062  * @cfg {Number} radius default 0
26063  * @cfg {String} locationName
26064  * @cfg {Boolean} draggable default true
26065  * @cfg {Boolean} enableAutocomplete default false
26066  * @cfg {Boolean} enableReverseGeocode default true
26067  * @cfg {String} markerTitle
26068  * 
26069  * @constructor
26070  * Create a new LocationPicker
26071  * @param {Object} config The config object
26072  */
26073
26074
26075 Roo.bootstrap.LocationPicker = function(config){
26076     
26077     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26078     
26079     this.addEvents({
26080         /**
26081          * @event initial
26082          * Fires when the picker initialized.
26083          * @param {Roo.bootstrap.LocationPicker} this
26084          * @param {Google Location} location
26085          */
26086         initial : true,
26087         /**
26088          * @event positionchanged
26089          * Fires when the picker position changed.
26090          * @param {Roo.bootstrap.LocationPicker} this
26091          * @param {Google Location} location
26092          */
26093         positionchanged : true,
26094         /**
26095          * @event resize
26096          * Fires when the map resize.
26097          * @param {Roo.bootstrap.LocationPicker} this
26098          */
26099         resize : true,
26100         /**
26101          * @event show
26102          * Fires when the map show.
26103          * @param {Roo.bootstrap.LocationPicker} this
26104          */
26105         show : true,
26106         /**
26107          * @event hide
26108          * Fires when the map hide.
26109          * @param {Roo.bootstrap.LocationPicker} this
26110          */
26111         hide : true,
26112         /**
26113          * @event mapClick
26114          * Fires when click the map.
26115          * @param {Roo.bootstrap.LocationPicker} this
26116          * @param {Map event} e
26117          */
26118         mapClick : true,
26119         /**
26120          * @event mapRightClick
26121          * Fires when right click the map.
26122          * @param {Roo.bootstrap.LocationPicker} this
26123          * @param {Map event} e
26124          */
26125         mapRightClick : true,
26126         /**
26127          * @event markerClick
26128          * Fires when click the marker.
26129          * @param {Roo.bootstrap.LocationPicker} this
26130          * @param {Map event} e
26131          */
26132         markerClick : true,
26133         /**
26134          * @event markerRightClick
26135          * Fires when right click the marker.
26136          * @param {Roo.bootstrap.LocationPicker} this
26137          * @param {Map event} e
26138          */
26139         markerRightClick : true,
26140         /**
26141          * @event OverlayViewDraw
26142          * Fires when OverlayView Draw
26143          * @param {Roo.bootstrap.LocationPicker} this
26144          */
26145         OverlayViewDraw : true,
26146         /**
26147          * @event OverlayViewOnAdd
26148          * Fires when OverlayView Draw
26149          * @param {Roo.bootstrap.LocationPicker} this
26150          */
26151         OverlayViewOnAdd : true,
26152         /**
26153          * @event OverlayViewOnRemove
26154          * Fires when OverlayView Draw
26155          * @param {Roo.bootstrap.LocationPicker} this
26156          */
26157         OverlayViewOnRemove : true,
26158         /**
26159          * @event OverlayViewShow
26160          * Fires when OverlayView Draw
26161          * @param {Roo.bootstrap.LocationPicker} this
26162          * @param {Pixel} cpx
26163          */
26164         OverlayViewShow : true,
26165         /**
26166          * @event OverlayViewHide
26167          * Fires when OverlayView Draw
26168          * @param {Roo.bootstrap.LocationPicker} this
26169          */
26170         OverlayViewHide : true,
26171         /**
26172          * @event loadexception
26173          * Fires when load google lib failed.
26174          * @param {Roo.bootstrap.LocationPicker} this
26175          */
26176         loadexception : true
26177     });
26178         
26179 };
26180
26181 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26182     
26183     gMapContext: false,
26184     
26185     latitude: 0,
26186     longitude: 0,
26187     zoom: 15,
26188     mapTypeId: false,
26189     mapTypeControl: false,
26190     disableDoubleClickZoom: false,
26191     scrollwheel: true,
26192     streetViewControl: false,
26193     radius: 0,
26194     locationName: '',
26195     draggable: true,
26196     enableAutocomplete: false,
26197     enableReverseGeocode: true,
26198     markerTitle: '',
26199     
26200     getAutoCreate: function()
26201     {
26202
26203         var cfg = {
26204             tag: 'div',
26205             cls: 'roo-location-picker'
26206         };
26207         
26208         return cfg
26209     },
26210     
26211     initEvents: function(ct, position)
26212     {       
26213         if(!this.el.getWidth() || this.isApplied()){
26214             return;
26215         }
26216         
26217         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26218         
26219         this.initial();
26220     },
26221     
26222     initial: function()
26223     {
26224         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26225             this.fireEvent('loadexception', this);
26226             return;
26227         }
26228         
26229         if(!this.mapTypeId){
26230             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26231         }
26232         
26233         this.gMapContext = this.GMapContext();
26234         
26235         this.initOverlayView();
26236         
26237         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26238         
26239         var _this = this;
26240                 
26241         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26242             _this.setPosition(_this.gMapContext.marker.position);
26243         });
26244         
26245         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26246             _this.fireEvent('mapClick', this, event);
26247             
26248         });
26249
26250         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26251             _this.fireEvent('mapRightClick', this, event);
26252             
26253         });
26254         
26255         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26256             _this.fireEvent('markerClick', this, event);
26257             
26258         });
26259
26260         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26261             _this.fireEvent('markerRightClick', this, event);
26262             
26263         });
26264         
26265         this.setPosition(this.gMapContext.location);
26266         
26267         this.fireEvent('initial', this, this.gMapContext.location);
26268     },
26269     
26270     initOverlayView: function()
26271     {
26272         var _this = this;
26273         
26274         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26275             
26276             draw: function()
26277             {
26278                 _this.fireEvent('OverlayViewDraw', _this);
26279             },
26280             
26281             onAdd: function()
26282             {
26283                 _this.fireEvent('OverlayViewOnAdd', _this);
26284             },
26285             
26286             onRemove: function()
26287             {
26288                 _this.fireEvent('OverlayViewOnRemove', _this);
26289             },
26290             
26291             show: function(cpx)
26292             {
26293                 _this.fireEvent('OverlayViewShow', _this, cpx);
26294             },
26295             
26296             hide: function()
26297             {
26298                 _this.fireEvent('OverlayViewHide', _this);
26299             }
26300             
26301         });
26302     },
26303     
26304     fromLatLngToContainerPixel: function(event)
26305     {
26306         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26307     },
26308     
26309     isApplied: function() 
26310     {
26311         return this.getGmapContext() == false ? false : true;
26312     },
26313     
26314     getGmapContext: function() 
26315     {
26316         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26317     },
26318     
26319     GMapContext: function() 
26320     {
26321         var position = new google.maps.LatLng(this.latitude, this.longitude);
26322         
26323         var _map = new google.maps.Map(this.el.dom, {
26324             center: position,
26325             zoom: this.zoom,
26326             mapTypeId: this.mapTypeId,
26327             mapTypeControl: this.mapTypeControl,
26328             disableDoubleClickZoom: this.disableDoubleClickZoom,
26329             scrollwheel: this.scrollwheel,
26330             streetViewControl: this.streetViewControl,
26331             locationName: this.locationName,
26332             draggable: this.draggable,
26333             enableAutocomplete: this.enableAutocomplete,
26334             enableReverseGeocode: this.enableReverseGeocode
26335         });
26336         
26337         var _marker = new google.maps.Marker({
26338             position: position,
26339             map: _map,
26340             title: this.markerTitle,
26341             draggable: this.draggable
26342         });
26343         
26344         return {
26345             map: _map,
26346             marker: _marker,
26347             circle: null,
26348             location: position,
26349             radius: this.radius,
26350             locationName: this.locationName,
26351             addressComponents: {
26352                 formatted_address: null,
26353                 addressLine1: null,
26354                 addressLine2: null,
26355                 streetName: null,
26356                 streetNumber: null,
26357                 city: null,
26358                 district: null,
26359                 state: null,
26360                 stateOrProvince: null
26361             },
26362             settings: this,
26363             domContainer: this.el.dom,
26364             geodecoder: new google.maps.Geocoder()
26365         };
26366     },
26367     
26368     drawCircle: function(center, radius, options) 
26369     {
26370         if (this.gMapContext.circle != null) {
26371             this.gMapContext.circle.setMap(null);
26372         }
26373         if (radius > 0) {
26374             radius *= 1;
26375             options = Roo.apply({}, options, {
26376                 strokeColor: "#0000FF",
26377                 strokeOpacity: .35,
26378                 strokeWeight: 2,
26379                 fillColor: "#0000FF",
26380                 fillOpacity: .2
26381             });
26382             
26383             options.map = this.gMapContext.map;
26384             options.radius = radius;
26385             options.center = center;
26386             this.gMapContext.circle = new google.maps.Circle(options);
26387             return this.gMapContext.circle;
26388         }
26389         
26390         return null;
26391     },
26392     
26393     setPosition: function(location) 
26394     {
26395         this.gMapContext.location = location;
26396         this.gMapContext.marker.setPosition(location);
26397         this.gMapContext.map.panTo(location);
26398         this.drawCircle(location, this.gMapContext.radius, {});
26399         
26400         var _this = this;
26401         
26402         if (this.gMapContext.settings.enableReverseGeocode) {
26403             this.gMapContext.geodecoder.geocode({
26404                 latLng: this.gMapContext.location
26405             }, function(results, status) {
26406                 
26407                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26408                     _this.gMapContext.locationName = results[0].formatted_address;
26409                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26410                     
26411                     _this.fireEvent('positionchanged', this, location);
26412                 }
26413             });
26414             
26415             return;
26416         }
26417         
26418         this.fireEvent('positionchanged', this, location);
26419     },
26420     
26421     resize: function()
26422     {
26423         google.maps.event.trigger(this.gMapContext.map, "resize");
26424         
26425         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26426         
26427         this.fireEvent('resize', this);
26428     },
26429     
26430     setPositionByLatLng: function(latitude, longitude)
26431     {
26432         this.setPosition(new google.maps.LatLng(latitude, longitude));
26433     },
26434     
26435     getCurrentPosition: function() 
26436     {
26437         return {
26438             latitude: this.gMapContext.location.lat(),
26439             longitude: this.gMapContext.location.lng()
26440         };
26441     },
26442     
26443     getAddressName: function() 
26444     {
26445         return this.gMapContext.locationName;
26446     },
26447     
26448     getAddressComponents: function() 
26449     {
26450         return this.gMapContext.addressComponents;
26451     },
26452     
26453     address_component_from_google_geocode: function(address_components) 
26454     {
26455         var result = {};
26456         
26457         for (var i = 0; i < address_components.length; i++) {
26458             var component = address_components[i];
26459             if (component.types.indexOf("postal_code") >= 0) {
26460                 result.postalCode = component.short_name;
26461             } else if (component.types.indexOf("street_number") >= 0) {
26462                 result.streetNumber = component.short_name;
26463             } else if (component.types.indexOf("route") >= 0) {
26464                 result.streetName = component.short_name;
26465             } else if (component.types.indexOf("neighborhood") >= 0) {
26466                 result.city = component.short_name;
26467             } else if (component.types.indexOf("locality") >= 0) {
26468                 result.city = component.short_name;
26469             } else if (component.types.indexOf("sublocality") >= 0) {
26470                 result.district = component.short_name;
26471             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26472                 result.stateOrProvince = component.short_name;
26473             } else if (component.types.indexOf("country") >= 0) {
26474                 result.country = component.short_name;
26475             }
26476         }
26477         
26478         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26479         result.addressLine2 = "";
26480         return result;
26481     },
26482     
26483     setZoomLevel: function(zoom)
26484     {
26485         this.gMapContext.map.setZoom(zoom);
26486     },
26487     
26488     show: function()
26489     {
26490         if(!this.el){
26491             return;
26492         }
26493         
26494         this.el.show();
26495         
26496         this.resize();
26497         
26498         this.fireEvent('show', this);
26499     },
26500     
26501     hide: function()
26502     {
26503         if(!this.el){
26504             return;
26505         }
26506         
26507         this.el.hide();
26508         
26509         this.fireEvent('hide', this);
26510     }
26511     
26512 });
26513
26514 Roo.apply(Roo.bootstrap.LocationPicker, {
26515     
26516     OverlayView : function(map, options)
26517     {
26518         options = options || {};
26519         
26520         this.setMap(map);
26521     }
26522     
26523     
26524 });/*
26525  * - LGPL
26526  *
26527  * Alert
26528  * 
26529  */
26530
26531 /**
26532  * @class Roo.bootstrap.Alert
26533  * @extends Roo.bootstrap.Component
26534  * Bootstrap Alert class
26535  * @cfg {String} title The title of alert
26536  * @cfg {String} html The content of alert
26537  * @cfg {String} weight (  success | info | warning | danger )
26538  * @cfg {String} faicon font-awesomeicon
26539  * 
26540  * @constructor
26541  * Create a new alert
26542  * @param {Object} config The config object
26543  */
26544
26545
26546 Roo.bootstrap.Alert = function(config){
26547     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26548     
26549 };
26550
26551 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26552     
26553     title: '',
26554     html: '',
26555     weight: false,
26556     faicon: false,
26557     
26558     getAutoCreate : function()
26559     {
26560         
26561         var cfg = {
26562             tag : 'div',
26563             cls : 'alert',
26564             cn : [
26565                 {
26566                     tag : 'i',
26567                     cls : 'roo-alert-icon'
26568                     
26569                 },
26570                 {
26571                     tag : 'b',
26572                     cls : 'roo-alert-title',
26573                     html : this.title
26574                 },
26575                 {
26576                     tag : 'span',
26577                     cls : 'roo-alert-text',
26578                     html : this.html
26579                 }
26580             ]
26581         };
26582         
26583         if(this.faicon){
26584             cfg.cn[0].cls += ' fa ' + this.faicon;
26585         }
26586         
26587         if(this.weight){
26588             cfg.cls += ' alert-' + this.weight;
26589         }
26590         
26591         return cfg;
26592     },
26593     
26594     initEvents: function() 
26595     {
26596         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26597     },
26598     
26599     setTitle : function(str)
26600     {
26601         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26602     },
26603     
26604     setText : function(str)
26605     {
26606         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26607     },
26608     
26609     setWeight : function(weight)
26610     {
26611         if(this.weight){
26612             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26613         }
26614         
26615         this.weight = weight;
26616         
26617         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26618     },
26619     
26620     setIcon : function(icon)
26621     {
26622         if(this.faicon){
26623             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26624         }
26625         
26626         this.faicon = icon;
26627         
26628         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26629     },
26630     
26631     hide: function() 
26632     {
26633         this.el.hide();   
26634     },
26635     
26636     show: function() 
26637     {  
26638         this.el.show();   
26639     }
26640     
26641 });
26642
26643  
26644 /*
26645 * Licence: LGPL
26646 */
26647
26648 /**
26649  * @class Roo.bootstrap.UploadCropbox
26650  * @extends Roo.bootstrap.Component
26651  * Bootstrap UploadCropbox class
26652  * @cfg {String} emptyText show when image has been loaded
26653  * @cfg {String} rotateNotify show when image too small to rotate
26654  * @cfg {Number} errorTimeout default 3000
26655  * @cfg {Number} minWidth default 300
26656  * @cfg {Number} minHeight default 300
26657  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26658  * @cfg {Boolean} isDocument (true|false) default false
26659  * @cfg {String} url action url
26660  * @cfg {String} paramName default 'imageUpload'
26661  * @cfg {String} method default POST
26662  * @cfg {Boolean} loadMask (true|false) default true
26663  * @cfg {Boolean} loadingText default 'Loading...'
26664  * 
26665  * @constructor
26666  * Create a new UploadCropbox
26667  * @param {Object} config The config object
26668  */
26669
26670 Roo.bootstrap.UploadCropbox = function(config){
26671     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26672     
26673     this.addEvents({
26674         /**
26675          * @event beforeselectfile
26676          * Fire before select file
26677          * @param {Roo.bootstrap.UploadCropbox} this
26678          */
26679         "beforeselectfile" : true,
26680         /**
26681          * @event initial
26682          * Fire after initEvent
26683          * @param {Roo.bootstrap.UploadCropbox} this
26684          */
26685         "initial" : true,
26686         /**
26687          * @event crop
26688          * Fire after initEvent
26689          * @param {Roo.bootstrap.UploadCropbox} this
26690          * @param {String} data
26691          */
26692         "crop" : true,
26693         /**
26694          * @event prepare
26695          * Fire when preparing the file data
26696          * @param {Roo.bootstrap.UploadCropbox} this
26697          * @param {Object} file
26698          */
26699         "prepare" : true,
26700         /**
26701          * @event exception
26702          * Fire when get exception
26703          * @param {Roo.bootstrap.UploadCropbox} this
26704          * @param {XMLHttpRequest} xhr
26705          */
26706         "exception" : true,
26707         /**
26708          * @event beforeloadcanvas
26709          * Fire before load the canvas
26710          * @param {Roo.bootstrap.UploadCropbox} this
26711          * @param {String} src
26712          */
26713         "beforeloadcanvas" : true,
26714         /**
26715          * @event trash
26716          * Fire when trash image
26717          * @param {Roo.bootstrap.UploadCropbox} this
26718          */
26719         "trash" : true,
26720         /**
26721          * @event download
26722          * Fire when download the image
26723          * @param {Roo.bootstrap.UploadCropbox} this
26724          */
26725         "download" : true,
26726         /**
26727          * @event footerbuttonclick
26728          * Fire when footerbuttonclick
26729          * @param {Roo.bootstrap.UploadCropbox} this
26730          * @param {String} type
26731          */
26732         "footerbuttonclick" : true,
26733         /**
26734          * @event resize
26735          * Fire when resize
26736          * @param {Roo.bootstrap.UploadCropbox} this
26737          */
26738         "resize" : true,
26739         /**
26740          * @event rotate
26741          * Fire when rotate the image
26742          * @param {Roo.bootstrap.UploadCropbox} this
26743          * @param {String} pos
26744          */
26745         "rotate" : true,
26746         /**
26747          * @event inspect
26748          * Fire when inspect the file
26749          * @param {Roo.bootstrap.UploadCropbox} this
26750          * @param {Object} file
26751          */
26752         "inspect" : true,
26753         /**
26754          * @event upload
26755          * Fire when xhr upload the file
26756          * @param {Roo.bootstrap.UploadCropbox} this
26757          * @param {Object} data
26758          */
26759         "upload" : true,
26760         /**
26761          * @event arrange
26762          * Fire when arrange the file data
26763          * @param {Roo.bootstrap.UploadCropbox} this
26764          * @param {Object} formData
26765          */
26766         "arrange" : true
26767     });
26768     
26769     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26770 };
26771
26772 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26773     
26774     emptyText : 'Click to upload image',
26775     rotateNotify : 'Image is too small to rotate',
26776     errorTimeout : 3000,
26777     scale : 0,
26778     baseScale : 1,
26779     rotate : 0,
26780     dragable : false,
26781     pinching : false,
26782     mouseX : 0,
26783     mouseY : 0,
26784     cropData : false,
26785     minWidth : 300,
26786     minHeight : 300,
26787     file : false,
26788     exif : {},
26789     baseRotate : 1,
26790     cropType : 'image/jpeg',
26791     buttons : false,
26792     canvasLoaded : false,
26793     isDocument : false,
26794     method : 'POST',
26795     paramName : 'imageUpload',
26796     loadMask : true,
26797     loadingText : 'Loading...',
26798     maskEl : false,
26799     
26800     getAutoCreate : function()
26801     {
26802         var cfg = {
26803             tag : 'div',
26804             cls : 'roo-upload-cropbox',
26805             cn : [
26806                 {
26807                     tag : 'input',
26808                     cls : 'roo-upload-cropbox-selector',
26809                     type : 'file'
26810                 },
26811                 {
26812                     tag : 'div',
26813                     cls : 'roo-upload-cropbox-body',
26814                     style : 'cursor:pointer',
26815                     cn : [
26816                         {
26817                             tag : 'div',
26818                             cls : 'roo-upload-cropbox-preview'
26819                         },
26820                         {
26821                             tag : 'div',
26822                             cls : 'roo-upload-cropbox-thumb'
26823                         },
26824                         {
26825                             tag : 'div',
26826                             cls : 'roo-upload-cropbox-empty-notify',
26827                             html : this.emptyText
26828                         },
26829                         {
26830                             tag : 'div',
26831                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26832                             html : this.rotateNotify
26833                         }
26834                     ]
26835                 },
26836                 {
26837                     tag : 'div',
26838                     cls : 'roo-upload-cropbox-footer',
26839                     cn : {
26840                         tag : 'div',
26841                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26842                         cn : []
26843                     }
26844                 }
26845             ]
26846         };
26847         
26848         return cfg;
26849     },
26850     
26851     onRender : function(ct, position)
26852     {
26853         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26854         
26855         if (this.buttons.length) {
26856             
26857             Roo.each(this.buttons, function(bb) {
26858                 
26859                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26860                 
26861                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26862                 
26863             }, this);
26864         }
26865         
26866         if(this.loadMask){
26867             this.maskEl = this.el;
26868         }
26869     },
26870     
26871     initEvents : function()
26872     {
26873         this.urlAPI = (window.createObjectURL && window) || 
26874                                 (window.URL && URL.revokeObjectURL && URL) || 
26875                                 (window.webkitURL && webkitURL);
26876                         
26877         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26878         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26879         
26880         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26881         this.selectorEl.hide();
26882         
26883         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26884         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26885         
26886         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26887         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26888         this.thumbEl.hide();
26889         
26890         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26891         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26892         
26893         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26894         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26895         this.errorEl.hide();
26896         
26897         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26898         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26899         this.footerEl.hide();
26900         
26901         this.setThumbBoxSize();
26902         
26903         this.bind();
26904         
26905         this.resize();
26906         
26907         this.fireEvent('initial', this);
26908     },
26909
26910     bind : function()
26911     {
26912         var _this = this;
26913         
26914         window.addEventListener("resize", function() { _this.resize(); } );
26915         
26916         this.bodyEl.on('click', this.beforeSelectFile, this);
26917         
26918         if(Roo.isTouch){
26919             this.bodyEl.on('touchstart', this.onTouchStart, this);
26920             this.bodyEl.on('touchmove', this.onTouchMove, this);
26921             this.bodyEl.on('touchend', this.onTouchEnd, this);
26922         }
26923         
26924         if(!Roo.isTouch){
26925             this.bodyEl.on('mousedown', this.onMouseDown, this);
26926             this.bodyEl.on('mousemove', this.onMouseMove, this);
26927             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26928             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26929             Roo.get(document).on('mouseup', this.onMouseUp, this);
26930         }
26931         
26932         this.selectorEl.on('change', this.onFileSelected, this);
26933     },
26934     
26935     reset : function()
26936     {    
26937         this.scale = 0;
26938         this.baseScale = 1;
26939         this.rotate = 0;
26940         this.baseRotate = 1;
26941         this.dragable = false;
26942         this.pinching = false;
26943         this.mouseX = 0;
26944         this.mouseY = 0;
26945         this.cropData = false;
26946         this.notifyEl.dom.innerHTML = this.emptyText;
26947         
26948         this.selectorEl.dom.value = '';
26949         
26950     },
26951     
26952     resize : function()
26953     {
26954         if(this.fireEvent('resize', this) != false){
26955             this.setThumbBoxPosition();
26956             this.setCanvasPosition();
26957         }
26958     },
26959     
26960     onFooterButtonClick : function(e, el, o, type)
26961     {
26962         switch (type) {
26963             case 'rotate-left' :
26964                 this.onRotateLeft(e);
26965                 break;
26966             case 'rotate-right' :
26967                 this.onRotateRight(e);
26968                 break;
26969             case 'picture' :
26970                 this.beforeSelectFile(e);
26971                 break;
26972             case 'trash' :
26973                 this.trash(e);
26974                 break;
26975             case 'crop' :
26976                 this.crop(e);
26977                 break;
26978             case 'download' :
26979                 this.download(e);
26980                 break;
26981             default :
26982                 break;
26983         }
26984         
26985         this.fireEvent('footerbuttonclick', this, type);
26986     },
26987     
26988     beforeSelectFile : function(e)
26989     {
26990         e.preventDefault();
26991         
26992         if(this.fireEvent('beforeselectfile', this) != false){
26993             this.selectorEl.dom.click();
26994         }
26995     },
26996     
26997     onFileSelected : function(e)
26998     {
26999         e.preventDefault();
27000         
27001         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27002             return;
27003         }
27004         
27005         var file = this.selectorEl.dom.files[0];
27006         
27007         if(this.fireEvent('inspect', this, file) != false){
27008             this.prepare(file);
27009         }
27010         
27011     },
27012     
27013     trash : function(e)
27014     {
27015         this.fireEvent('trash', this);
27016     },
27017     
27018     download : function(e)
27019     {
27020         this.fireEvent('download', this);
27021     },
27022     
27023     loadCanvas : function(src)
27024     {   
27025         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27026             
27027             this.reset();
27028             
27029             this.imageEl = document.createElement('img');
27030             
27031             var _this = this;
27032             
27033             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27034             
27035             this.imageEl.src = src;
27036         }
27037     },
27038     
27039     onLoadCanvas : function()
27040     {   
27041         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27042         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27043         
27044         this.bodyEl.un('click', this.beforeSelectFile, this);
27045         
27046         this.notifyEl.hide();
27047         this.thumbEl.show();
27048         this.footerEl.show();
27049         
27050         this.baseRotateLevel();
27051         
27052         if(this.isDocument){
27053             this.setThumbBoxSize();
27054         }
27055         
27056         this.setThumbBoxPosition();
27057         
27058         this.baseScaleLevel();
27059         
27060         this.draw();
27061         
27062         this.resize();
27063         
27064         this.canvasLoaded = true;
27065         
27066         if(this.loadMask){
27067             this.maskEl.unmask();
27068         }
27069         
27070     },
27071     
27072     setCanvasPosition : function()
27073     {   
27074         if(!this.canvasEl){
27075             return;
27076         }
27077         
27078         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27079         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27080         
27081         this.previewEl.setLeft(pw);
27082         this.previewEl.setTop(ph);
27083         
27084     },
27085     
27086     onMouseDown : function(e)
27087     {   
27088         e.stopEvent();
27089         
27090         this.dragable = true;
27091         this.pinching = false;
27092         
27093         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27094             this.dragable = false;
27095             return;
27096         }
27097         
27098         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27099         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27100         
27101     },
27102     
27103     onMouseMove : function(e)
27104     {   
27105         e.stopEvent();
27106         
27107         if(!this.canvasLoaded){
27108             return;
27109         }
27110         
27111         if (!this.dragable){
27112             return;
27113         }
27114         
27115         var minX = Math.ceil(this.thumbEl.getLeft(true));
27116         var minY = Math.ceil(this.thumbEl.getTop(true));
27117         
27118         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27119         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27120         
27121         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27122         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27123         
27124         x = x - this.mouseX;
27125         y = y - this.mouseY;
27126         
27127         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27128         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27129         
27130         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27131         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27132         
27133         this.previewEl.setLeft(bgX);
27134         this.previewEl.setTop(bgY);
27135         
27136         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27137         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27138     },
27139     
27140     onMouseUp : function(e)
27141     {   
27142         e.stopEvent();
27143         
27144         this.dragable = false;
27145     },
27146     
27147     onMouseWheel : function(e)
27148     {   
27149         e.stopEvent();
27150         
27151         this.startScale = this.scale;
27152         
27153         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27154         
27155         if(!this.zoomable()){
27156             this.scale = this.startScale;
27157             return;
27158         }
27159         
27160         this.draw();
27161         
27162         return;
27163     },
27164     
27165     zoomable : function()
27166     {
27167         var minScale = this.thumbEl.getWidth() / this.minWidth;
27168         
27169         if(this.minWidth < this.minHeight){
27170             minScale = this.thumbEl.getHeight() / this.minHeight;
27171         }
27172         
27173         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27174         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27175         
27176         if(
27177                 this.isDocument &&
27178                 (this.rotate == 0 || this.rotate == 180) && 
27179                 (
27180                     width > this.imageEl.OriginWidth || 
27181                     height > this.imageEl.OriginHeight ||
27182                     (width < this.minWidth && height < this.minHeight)
27183                 )
27184         ){
27185             return false;
27186         }
27187         
27188         if(
27189                 this.isDocument &&
27190                 (this.rotate == 90 || this.rotate == 270) && 
27191                 (
27192                     width > this.imageEl.OriginWidth || 
27193                     height > this.imageEl.OriginHeight ||
27194                     (width < this.minHeight && height < this.minWidth)
27195                 )
27196         ){
27197             return false;
27198         }
27199         
27200         if(
27201                 !this.isDocument &&
27202                 (this.rotate == 0 || this.rotate == 180) && 
27203                 (
27204                     width < this.minWidth || 
27205                     width > this.imageEl.OriginWidth || 
27206                     height < this.minHeight || 
27207                     height > this.imageEl.OriginHeight
27208                 )
27209         ){
27210             return false;
27211         }
27212         
27213         if(
27214                 !this.isDocument &&
27215                 (this.rotate == 90 || this.rotate == 270) && 
27216                 (
27217                     width < this.minHeight || 
27218                     width > this.imageEl.OriginWidth || 
27219                     height < this.minWidth || 
27220                     height > this.imageEl.OriginHeight
27221                 )
27222         ){
27223             return false;
27224         }
27225         
27226         return true;
27227         
27228     },
27229     
27230     onRotateLeft : function(e)
27231     {   
27232         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27233             
27234             var minScale = this.thumbEl.getWidth() / this.minWidth;
27235             
27236             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27237             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27238             
27239             this.startScale = this.scale;
27240             
27241             while (this.getScaleLevel() < minScale){
27242             
27243                 this.scale = this.scale + 1;
27244                 
27245                 if(!this.zoomable()){
27246                     break;
27247                 }
27248                 
27249                 if(
27250                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27251                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27252                 ){
27253                     continue;
27254                 }
27255                 
27256                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27257
27258                 this.draw();
27259                 
27260                 return;
27261             }
27262             
27263             this.scale = this.startScale;
27264             
27265             this.onRotateFail();
27266             
27267             return false;
27268         }
27269         
27270         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27271
27272         if(this.isDocument){
27273             this.setThumbBoxSize();
27274             this.setThumbBoxPosition();
27275             this.setCanvasPosition();
27276         }
27277         
27278         this.draw();
27279         
27280         this.fireEvent('rotate', this, 'left');
27281         
27282     },
27283     
27284     onRotateRight : function(e)
27285     {
27286         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27287             
27288             var minScale = this.thumbEl.getWidth() / this.minWidth;
27289         
27290             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27291             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27292             
27293             this.startScale = this.scale;
27294             
27295             while (this.getScaleLevel() < minScale){
27296             
27297                 this.scale = this.scale + 1;
27298                 
27299                 if(!this.zoomable()){
27300                     break;
27301                 }
27302                 
27303                 if(
27304                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27305                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27306                 ){
27307                     continue;
27308                 }
27309                 
27310                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27311
27312                 this.draw();
27313                 
27314                 return;
27315             }
27316             
27317             this.scale = this.startScale;
27318             
27319             this.onRotateFail();
27320             
27321             return false;
27322         }
27323         
27324         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27325
27326         if(this.isDocument){
27327             this.setThumbBoxSize();
27328             this.setThumbBoxPosition();
27329             this.setCanvasPosition();
27330         }
27331         
27332         this.draw();
27333         
27334         this.fireEvent('rotate', this, 'right');
27335     },
27336     
27337     onRotateFail : function()
27338     {
27339         this.errorEl.show(true);
27340         
27341         var _this = this;
27342         
27343         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27344     },
27345     
27346     draw : function()
27347     {
27348         this.previewEl.dom.innerHTML = '';
27349         
27350         var canvasEl = document.createElement("canvas");
27351         
27352         var contextEl = canvasEl.getContext("2d");
27353         
27354         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27355         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27356         var center = this.imageEl.OriginWidth / 2;
27357         
27358         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27359             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27360             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27361             center = this.imageEl.OriginHeight / 2;
27362         }
27363         
27364         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27365         
27366         contextEl.translate(center, center);
27367         contextEl.rotate(this.rotate * Math.PI / 180);
27368
27369         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27370         
27371         this.canvasEl = document.createElement("canvas");
27372         
27373         this.contextEl = this.canvasEl.getContext("2d");
27374         
27375         switch (this.rotate) {
27376             case 0 :
27377                 
27378                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27379                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27380                 
27381                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27382                 
27383                 break;
27384             case 90 : 
27385                 
27386                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27387                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27388                 
27389                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27390                     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);
27391                     break;
27392                 }
27393                 
27394                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27395                 
27396                 break;
27397             case 180 :
27398                 
27399                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27400                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27401                 
27402                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27403                     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);
27404                     break;
27405                 }
27406                 
27407                 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);
27408                 
27409                 break;
27410             case 270 :
27411                 
27412                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27413                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27414         
27415                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27416                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27417                     break;
27418                 }
27419                 
27420                 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);
27421                 
27422                 break;
27423             default : 
27424                 break;
27425         }
27426         
27427         this.previewEl.appendChild(this.canvasEl);
27428         
27429         this.setCanvasPosition();
27430     },
27431     
27432     crop : function()
27433     {
27434         if(!this.canvasLoaded){
27435             return;
27436         }
27437         
27438         var imageCanvas = document.createElement("canvas");
27439         
27440         var imageContext = imageCanvas.getContext("2d");
27441         
27442         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27443         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27444         
27445         var center = imageCanvas.width / 2;
27446         
27447         imageContext.translate(center, center);
27448         
27449         imageContext.rotate(this.rotate * Math.PI / 180);
27450         
27451         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27452         
27453         var canvas = document.createElement("canvas");
27454         
27455         var context = canvas.getContext("2d");
27456                 
27457         canvas.width = this.minWidth;
27458         canvas.height = this.minHeight;
27459
27460         switch (this.rotate) {
27461             case 0 :
27462                 
27463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27465                 
27466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27468                 
27469                 var targetWidth = this.minWidth - 2 * x;
27470                 var targetHeight = this.minHeight - 2 * y;
27471                 
27472                 var scale = 1;
27473                 
27474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27475                     scale = targetWidth / width;
27476                 }
27477                 
27478                 if(x > 0 && y == 0){
27479                     scale = targetHeight / height;
27480                 }
27481                 
27482                 if(x > 0 && y > 0){
27483                     scale = targetWidth / width;
27484                     
27485                     if(width < height){
27486                         scale = targetHeight / height;
27487                     }
27488                 }
27489                 
27490                 context.scale(scale, scale);
27491                 
27492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27494
27495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27497
27498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27499                 
27500                 break;
27501             case 90 : 
27502                 
27503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27505                 
27506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27508                 
27509                 var targetWidth = this.minWidth - 2 * x;
27510                 var targetHeight = this.minHeight - 2 * y;
27511                 
27512                 var scale = 1;
27513                 
27514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27515                     scale = targetWidth / width;
27516                 }
27517                 
27518                 if(x > 0 && y == 0){
27519                     scale = targetHeight / height;
27520                 }
27521                 
27522                 if(x > 0 && y > 0){
27523                     scale = targetWidth / width;
27524                     
27525                     if(width < height){
27526                         scale = targetHeight / height;
27527                     }
27528                 }
27529                 
27530                 context.scale(scale, scale);
27531                 
27532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27534
27535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27537                 
27538                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27539                 
27540                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27541                 
27542                 break;
27543             case 180 :
27544                 
27545                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27546                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27547                 
27548                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27549                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27550                 
27551                 var targetWidth = this.minWidth - 2 * x;
27552                 var targetHeight = this.minHeight - 2 * y;
27553                 
27554                 var scale = 1;
27555                 
27556                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27557                     scale = targetWidth / width;
27558                 }
27559                 
27560                 if(x > 0 && y == 0){
27561                     scale = targetHeight / height;
27562                 }
27563                 
27564                 if(x > 0 && y > 0){
27565                     scale = targetWidth / width;
27566                     
27567                     if(width < height){
27568                         scale = targetHeight / height;
27569                     }
27570                 }
27571                 
27572                 context.scale(scale, scale);
27573                 
27574                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27575                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27576
27577                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27578                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27579
27580                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27581                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27582                 
27583                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27584                 
27585                 break;
27586             case 270 :
27587                 
27588                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27589                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27590                 
27591                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27592                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27593                 
27594                 var targetWidth = this.minWidth - 2 * x;
27595                 var targetHeight = this.minHeight - 2 * y;
27596                 
27597                 var scale = 1;
27598                 
27599                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27600                     scale = targetWidth / width;
27601                 }
27602                 
27603                 if(x > 0 && y == 0){
27604                     scale = targetHeight / height;
27605                 }
27606                 
27607                 if(x > 0 && y > 0){
27608                     scale = targetWidth / width;
27609                     
27610                     if(width < height){
27611                         scale = targetHeight / height;
27612                     }
27613                 }
27614                 
27615                 context.scale(scale, scale);
27616                 
27617                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27618                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27619
27620                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27621                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27622                 
27623                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27624                 
27625                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27626                 
27627                 break;
27628             default : 
27629                 break;
27630         }
27631         
27632         this.cropData = canvas.toDataURL(this.cropType);
27633         
27634         if(this.fireEvent('crop', this, this.cropData) !== false){
27635             this.process(this.file, this.cropData);
27636         }
27637         
27638         return;
27639         
27640     },
27641     
27642     setThumbBoxSize : function()
27643     {
27644         var width, height;
27645         
27646         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27647             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27648             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27649             
27650             this.minWidth = width;
27651             this.minHeight = height;
27652             
27653             if(this.rotate == 90 || this.rotate == 270){
27654                 this.minWidth = height;
27655                 this.minHeight = width;
27656             }
27657         }
27658         
27659         height = 300;
27660         width = Math.ceil(this.minWidth * height / this.minHeight);
27661         
27662         if(this.minWidth > this.minHeight){
27663             width = 300;
27664             height = Math.ceil(this.minHeight * width / this.minWidth);
27665         }
27666         
27667         this.thumbEl.setStyle({
27668             width : width + 'px',
27669             height : height + 'px'
27670         });
27671
27672         return;
27673             
27674     },
27675     
27676     setThumbBoxPosition : function()
27677     {
27678         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27679         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27680         
27681         this.thumbEl.setLeft(x);
27682         this.thumbEl.setTop(y);
27683         
27684     },
27685     
27686     baseRotateLevel : function()
27687     {
27688         this.baseRotate = 1;
27689         
27690         if(
27691                 typeof(this.exif) != 'undefined' &&
27692                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27693                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27694         ){
27695             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27696         }
27697         
27698         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27699         
27700     },
27701     
27702     baseScaleLevel : function()
27703     {
27704         var width, height;
27705         
27706         if(this.isDocument){
27707             
27708             if(this.baseRotate == 6 || this.baseRotate == 8){
27709             
27710                 height = this.thumbEl.getHeight();
27711                 this.baseScale = height / this.imageEl.OriginWidth;
27712
27713                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27714                     width = this.thumbEl.getWidth();
27715                     this.baseScale = width / this.imageEl.OriginHeight;
27716                 }
27717
27718                 return;
27719             }
27720
27721             height = this.thumbEl.getHeight();
27722             this.baseScale = height / this.imageEl.OriginHeight;
27723
27724             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27725                 width = this.thumbEl.getWidth();
27726                 this.baseScale = width / this.imageEl.OriginWidth;
27727             }
27728
27729             return;
27730         }
27731         
27732         if(this.baseRotate == 6 || this.baseRotate == 8){
27733             
27734             width = this.thumbEl.getHeight();
27735             this.baseScale = width / this.imageEl.OriginHeight;
27736             
27737             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27738                 height = this.thumbEl.getWidth();
27739                 this.baseScale = height / this.imageEl.OriginHeight;
27740             }
27741             
27742             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27743                 height = this.thumbEl.getWidth();
27744                 this.baseScale = height / this.imageEl.OriginHeight;
27745                 
27746                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27747                     width = this.thumbEl.getHeight();
27748                     this.baseScale = width / this.imageEl.OriginWidth;
27749                 }
27750             }
27751             
27752             return;
27753         }
27754         
27755         width = this.thumbEl.getWidth();
27756         this.baseScale = width / this.imageEl.OriginWidth;
27757         
27758         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27759             height = this.thumbEl.getHeight();
27760             this.baseScale = height / this.imageEl.OriginHeight;
27761         }
27762         
27763         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27764             
27765             height = this.thumbEl.getHeight();
27766             this.baseScale = height / this.imageEl.OriginHeight;
27767             
27768             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27769                 width = this.thumbEl.getWidth();
27770                 this.baseScale = width / this.imageEl.OriginWidth;
27771             }
27772             
27773         }
27774         
27775         return;
27776     },
27777     
27778     getScaleLevel : function()
27779     {
27780         return this.baseScale * Math.pow(1.1, this.scale);
27781     },
27782     
27783     onTouchStart : function(e)
27784     {
27785         if(!this.canvasLoaded){
27786             this.beforeSelectFile(e);
27787             return;
27788         }
27789         
27790         var touches = e.browserEvent.touches;
27791         
27792         if(!touches){
27793             return;
27794         }
27795         
27796         if(touches.length == 1){
27797             this.onMouseDown(e);
27798             return;
27799         }
27800         
27801         if(touches.length != 2){
27802             return;
27803         }
27804         
27805         var coords = [];
27806         
27807         for(var i = 0, finger; finger = touches[i]; i++){
27808             coords.push(finger.pageX, finger.pageY);
27809         }
27810         
27811         var x = Math.pow(coords[0] - coords[2], 2);
27812         var y = Math.pow(coords[1] - coords[3], 2);
27813         
27814         this.startDistance = Math.sqrt(x + y);
27815         
27816         this.startScale = this.scale;
27817         
27818         this.pinching = true;
27819         this.dragable = false;
27820         
27821     },
27822     
27823     onTouchMove : function(e)
27824     {
27825         if(!this.pinching && !this.dragable){
27826             return;
27827         }
27828         
27829         var touches = e.browserEvent.touches;
27830         
27831         if(!touches){
27832             return;
27833         }
27834         
27835         if(this.dragable){
27836             this.onMouseMove(e);
27837             return;
27838         }
27839         
27840         var coords = [];
27841         
27842         for(var i = 0, finger; finger = touches[i]; i++){
27843             coords.push(finger.pageX, finger.pageY);
27844         }
27845         
27846         var x = Math.pow(coords[0] - coords[2], 2);
27847         var y = Math.pow(coords[1] - coords[3], 2);
27848         
27849         this.endDistance = Math.sqrt(x + y);
27850         
27851         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27852         
27853         if(!this.zoomable()){
27854             this.scale = this.startScale;
27855             return;
27856         }
27857         
27858         this.draw();
27859         
27860     },
27861     
27862     onTouchEnd : function(e)
27863     {
27864         this.pinching = false;
27865         this.dragable = false;
27866         
27867     },
27868     
27869     process : function(file, crop)
27870     {
27871         if(this.loadMask){
27872             this.maskEl.mask(this.loadingText);
27873         }
27874         
27875         this.xhr = new XMLHttpRequest();
27876         
27877         file.xhr = this.xhr;
27878
27879         this.xhr.open(this.method, this.url, true);
27880         
27881         var headers = {
27882             "Accept": "application/json",
27883             "Cache-Control": "no-cache",
27884             "X-Requested-With": "XMLHttpRequest"
27885         };
27886         
27887         for (var headerName in headers) {
27888             var headerValue = headers[headerName];
27889             if (headerValue) {
27890                 this.xhr.setRequestHeader(headerName, headerValue);
27891             }
27892         }
27893         
27894         var _this = this;
27895         
27896         this.xhr.onload = function()
27897         {
27898             _this.xhrOnLoad(_this.xhr);
27899         }
27900         
27901         this.xhr.onerror = function()
27902         {
27903             _this.xhrOnError(_this.xhr);
27904         }
27905         
27906         var formData = new FormData();
27907
27908         formData.append('returnHTML', 'NO');
27909         
27910         if(crop){
27911             formData.append('crop', crop);
27912         }
27913         
27914         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27915             formData.append(this.paramName, file, file.name);
27916         }
27917         
27918         if(typeof(file.filename) != 'undefined'){
27919             formData.append('filename', file.filename);
27920         }
27921         
27922         if(typeof(file.mimetype) != 'undefined'){
27923             formData.append('mimetype', file.mimetype);
27924         }
27925         
27926         if(this.fireEvent('arrange', this, formData) != false){
27927             this.xhr.send(formData);
27928         };
27929     },
27930     
27931     xhrOnLoad : function(xhr)
27932     {
27933         if(this.loadMask){
27934             this.maskEl.unmask();
27935         }
27936         
27937         if (xhr.readyState !== 4) {
27938             this.fireEvent('exception', this, xhr);
27939             return;
27940         }
27941
27942         var response = Roo.decode(xhr.responseText);
27943         
27944         if(!response.success){
27945             this.fireEvent('exception', this, xhr);
27946             return;
27947         }
27948         
27949         var response = Roo.decode(xhr.responseText);
27950         
27951         this.fireEvent('upload', this, response);
27952         
27953     },
27954     
27955     xhrOnError : function()
27956     {
27957         if(this.loadMask){
27958             this.maskEl.unmask();
27959         }
27960         
27961         Roo.log('xhr on error');
27962         
27963         var response = Roo.decode(xhr.responseText);
27964           
27965         Roo.log(response);
27966         
27967     },
27968     
27969     prepare : function(file)
27970     {   
27971         if(this.loadMask){
27972             this.maskEl.mask(this.loadingText);
27973         }
27974         
27975         this.file = false;
27976         this.exif = {};
27977         
27978         if(typeof(file) === 'string'){
27979             this.loadCanvas(file);
27980             return;
27981         }
27982         
27983         if(!file || !this.urlAPI){
27984             return;
27985         }
27986         
27987         this.file = file;
27988         this.cropType = file.type;
27989         
27990         var _this = this;
27991         
27992         if(this.fireEvent('prepare', this, this.file) != false){
27993             
27994             var reader = new FileReader();
27995             
27996             reader.onload = function (e) {
27997                 if (e.target.error) {
27998                     Roo.log(e.target.error);
27999                     return;
28000                 }
28001                 
28002                 var buffer = e.target.result,
28003                     dataView = new DataView(buffer),
28004                     offset = 2,
28005                     maxOffset = dataView.byteLength - 4,
28006                     markerBytes,
28007                     markerLength;
28008                 
28009                 if (dataView.getUint16(0) === 0xffd8) {
28010                     while (offset < maxOffset) {
28011                         markerBytes = dataView.getUint16(offset);
28012                         
28013                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28014                             markerLength = dataView.getUint16(offset + 2) + 2;
28015                             if (offset + markerLength > dataView.byteLength) {
28016                                 Roo.log('Invalid meta data: Invalid segment size.');
28017                                 break;
28018                             }
28019                             
28020                             if(markerBytes == 0xffe1){
28021                                 _this.parseExifData(
28022                                     dataView,
28023                                     offset,
28024                                     markerLength
28025                                 );
28026                             }
28027                             
28028                             offset += markerLength;
28029                             
28030                             continue;
28031                         }
28032                         
28033                         break;
28034                     }
28035                     
28036                 }
28037                 
28038                 var url = _this.urlAPI.createObjectURL(_this.file);
28039                 
28040                 _this.loadCanvas(url);
28041                 
28042                 return;
28043             }
28044             
28045             reader.readAsArrayBuffer(this.file);
28046             
28047         }
28048         
28049     },
28050     
28051     parseExifData : function(dataView, offset, length)
28052     {
28053         var tiffOffset = offset + 10,
28054             littleEndian,
28055             dirOffset;
28056     
28057         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28058             // No Exif data, might be XMP data instead
28059             return;
28060         }
28061         
28062         // Check for the ASCII code for "Exif" (0x45786966):
28063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28064             // No Exif data, might be XMP data instead
28065             return;
28066         }
28067         if (tiffOffset + 8 > dataView.byteLength) {
28068             Roo.log('Invalid Exif data: Invalid segment size.');
28069             return;
28070         }
28071         // Check for the two null bytes:
28072         if (dataView.getUint16(offset + 8) !== 0x0000) {
28073             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28074             return;
28075         }
28076         // Check the byte alignment:
28077         switch (dataView.getUint16(tiffOffset)) {
28078         case 0x4949:
28079             littleEndian = true;
28080             break;
28081         case 0x4D4D:
28082             littleEndian = false;
28083             break;
28084         default:
28085             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28086             return;
28087         }
28088         // Check for the TIFF tag marker (0x002A):
28089         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28090             Roo.log('Invalid Exif data: Missing TIFF marker.');
28091             return;
28092         }
28093         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28094         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28095         
28096         this.parseExifTags(
28097             dataView,
28098             tiffOffset,
28099             tiffOffset + dirOffset,
28100             littleEndian
28101         );
28102     },
28103     
28104     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28105     {
28106         var tagsNumber,
28107             dirEndOffset,
28108             i;
28109         if (dirOffset + 6 > dataView.byteLength) {
28110             Roo.log('Invalid Exif data: Invalid directory offset.');
28111             return;
28112         }
28113         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28114         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28115         if (dirEndOffset + 4 > dataView.byteLength) {
28116             Roo.log('Invalid Exif data: Invalid directory size.');
28117             return;
28118         }
28119         for (i = 0; i < tagsNumber; i += 1) {
28120             this.parseExifTag(
28121                 dataView,
28122                 tiffOffset,
28123                 dirOffset + 2 + 12 * i, // tag offset
28124                 littleEndian
28125             );
28126         }
28127         // Return the offset to the next directory:
28128         return dataView.getUint32(dirEndOffset, littleEndian);
28129     },
28130     
28131     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28132     {
28133         var tag = dataView.getUint16(offset, littleEndian);
28134         
28135         this.exif[tag] = this.getExifValue(
28136             dataView,
28137             tiffOffset,
28138             offset,
28139             dataView.getUint16(offset + 2, littleEndian), // tag type
28140             dataView.getUint32(offset + 4, littleEndian), // tag length
28141             littleEndian
28142         );
28143     },
28144     
28145     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28146     {
28147         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28148             tagSize,
28149             dataOffset,
28150             values,
28151             i,
28152             str,
28153             c;
28154     
28155         if (!tagType) {
28156             Roo.log('Invalid Exif data: Invalid tag type.');
28157             return;
28158         }
28159         
28160         tagSize = tagType.size * length;
28161         // Determine if the value is contained in the dataOffset bytes,
28162         // or if the value at the dataOffset is a pointer to the actual data:
28163         dataOffset = tagSize > 4 ?
28164                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28165         if (dataOffset + tagSize > dataView.byteLength) {
28166             Roo.log('Invalid Exif data: Invalid data offset.');
28167             return;
28168         }
28169         if (length === 1) {
28170             return tagType.getValue(dataView, dataOffset, littleEndian);
28171         }
28172         values = [];
28173         for (i = 0; i < length; i += 1) {
28174             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28175         }
28176         
28177         if (tagType.ascii) {
28178             str = '';
28179             // Concatenate the chars:
28180             for (i = 0; i < values.length; i += 1) {
28181                 c = values[i];
28182                 // Ignore the terminating NULL byte(s):
28183                 if (c === '\u0000') {
28184                     break;
28185                 }
28186                 str += c;
28187             }
28188             return str;
28189         }
28190         return values;
28191     }
28192     
28193 });
28194
28195 Roo.apply(Roo.bootstrap.UploadCropbox, {
28196     tags : {
28197         'Orientation': 0x0112
28198     },
28199     
28200     Orientation: {
28201             1: 0, //'top-left',
28202 //            2: 'top-right',
28203             3: 180, //'bottom-right',
28204 //            4: 'bottom-left',
28205 //            5: 'left-top',
28206             6: 90, //'right-top',
28207 //            7: 'right-bottom',
28208             8: 270 //'left-bottom'
28209     },
28210     
28211     exifTagTypes : {
28212         // byte, 8-bit unsigned int:
28213         1: {
28214             getValue: function (dataView, dataOffset) {
28215                 return dataView.getUint8(dataOffset);
28216             },
28217             size: 1
28218         },
28219         // ascii, 8-bit byte:
28220         2: {
28221             getValue: function (dataView, dataOffset) {
28222                 return String.fromCharCode(dataView.getUint8(dataOffset));
28223             },
28224             size: 1,
28225             ascii: true
28226         },
28227         // short, 16 bit int:
28228         3: {
28229             getValue: function (dataView, dataOffset, littleEndian) {
28230                 return dataView.getUint16(dataOffset, littleEndian);
28231             },
28232             size: 2
28233         },
28234         // long, 32 bit int:
28235         4: {
28236             getValue: function (dataView, dataOffset, littleEndian) {
28237                 return dataView.getUint32(dataOffset, littleEndian);
28238             },
28239             size: 4
28240         },
28241         // rational = two long values, first is numerator, second is denominator:
28242         5: {
28243             getValue: function (dataView, dataOffset, littleEndian) {
28244                 return dataView.getUint32(dataOffset, littleEndian) /
28245                     dataView.getUint32(dataOffset + 4, littleEndian);
28246             },
28247             size: 8
28248         },
28249         // slong, 32 bit signed int:
28250         9: {
28251             getValue: function (dataView, dataOffset, littleEndian) {
28252                 return dataView.getInt32(dataOffset, littleEndian);
28253             },
28254             size: 4
28255         },
28256         // srational, two slongs, first is numerator, second is denominator:
28257         10: {
28258             getValue: function (dataView, dataOffset, littleEndian) {
28259                 return dataView.getInt32(dataOffset, littleEndian) /
28260                     dataView.getInt32(dataOffset + 4, littleEndian);
28261             },
28262             size: 8
28263         }
28264     },
28265     
28266     footer : {
28267         STANDARD : [
28268             {
28269                 tag : 'div',
28270                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28271                 action : 'rotate-left',
28272                 cn : [
28273                     {
28274                         tag : 'button',
28275                         cls : 'btn btn-default',
28276                         html : '<i class="fa fa-undo"></i>'
28277                     }
28278                 ]
28279             },
28280             {
28281                 tag : 'div',
28282                 cls : 'btn-group roo-upload-cropbox-picture',
28283                 action : 'picture',
28284                 cn : [
28285                     {
28286                         tag : 'button',
28287                         cls : 'btn btn-default',
28288                         html : '<i class="fa fa-picture-o"></i>'
28289                     }
28290                 ]
28291             },
28292             {
28293                 tag : 'div',
28294                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28295                 action : 'rotate-right',
28296                 cn : [
28297                     {
28298                         tag : 'button',
28299                         cls : 'btn btn-default',
28300                         html : '<i class="fa fa-repeat"></i>'
28301                     }
28302                 ]
28303             }
28304         ],
28305         DOCUMENT : [
28306             {
28307                 tag : 'div',
28308                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28309                 action : 'rotate-left',
28310                 cn : [
28311                     {
28312                         tag : 'button',
28313                         cls : 'btn btn-default',
28314                         html : '<i class="fa fa-undo"></i>'
28315                     }
28316                 ]
28317             },
28318             {
28319                 tag : 'div',
28320                 cls : 'btn-group roo-upload-cropbox-download',
28321                 action : 'download',
28322                 cn : [
28323                     {
28324                         tag : 'button',
28325                         cls : 'btn btn-default',
28326                         html : '<i class="fa fa-download"></i>'
28327                     }
28328                 ]
28329             },
28330             {
28331                 tag : 'div',
28332                 cls : 'btn-group roo-upload-cropbox-crop',
28333                 action : 'crop',
28334                 cn : [
28335                     {
28336                         tag : 'button',
28337                         cls : 'btn btn-default',
28338                         html : '<i class="fa fa-crop"></i>'
28339                     }
28340                 ]
28341             },
28342             {
28343                 tag : 'div',
28344                 cls : 'btn-group roo-upload-cropbox-trash',
28345                 action : 'trash',
28346                 cn : [
28347                     {
28348                         tag : 'button',
28349                         cls : 'btn btn-default',
28350                         html : '<i class="fa fa-trash"></i>'
28351                     }
28352                 ]
28353             },
28354             {
28355                 tag : 'div',
28356                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28357                 action : 'rotate-right',
28358                 cn : [
28359                     {
28360                         tag : 'button',
28361                         cls : 'btn btn-default',
28362                         html : '<i class="fa fa-repeat"></i>'
28363                     }
28364                 ]
28365             }
28366         ],
28367         ROTATOR : [
28368             {
28369                 tag : 'div',
28370                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28371                 action : 'rotate-left',
28372                 cn : [
28373                     {
28374                         tag : 'button',
28375                         cls : 'btn btn-default',
28376                         html : '<i class="fa fa-undo"></i>'
28377                     }
28378                 ]
28379             },
28380             {
28381                 tag : 'div',
28382                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28383                 action : 'rotate-right',
28384                 cn : [
28385                     {
28386                         tag : 'button',
28387                         cls : 'btn btn-default',
28388                         html : '<i class="fa fa-repeat"></i>'
28389                     }
28390                 ]
28391             }
28392         ]
28393     }
28394 });
28395
28396 /*
28397 * Licence: LGPL
28398 */
28399
28400 /**
28401  * @class Roo.bootstrap.DocumentManager
28402  * @extends Roo.bootstrap.Component
28403  * Bootstrap DocumentManager class
28404  * @cfg {String} paramName default 'imageUpload'
28405  * @cfg {String} toolTipName default 'filename'
28406  * @cfg {String} method default POST
28407  * @cfg {String} url action url
28408  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28409  * @cfg {Boolean} multiple multiple upload default true
28410  * @cfg {Number} thumbSize default 300
28411  * @cfg {String} fieldLabel
28412  * @cfg {Number} labelWidth default 4
28413  * @cfg {String} labelAlign (left|top) default left
28414  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28415 * @cfg {Number} labellg set the width of label (1-12)
28416  * @cfg {Number} labelmd set the width of label (1-12)
28417  * @cfg {Number} labelsm set the width of label (1-12)
28418  * @cfg {Number} labelxs set the width of label (1-12)
28419  * 
28420  * @constructor
28421  * Create a new DocumentManager
28422  * @param {Object} config The config object
28423  */
28424
28425 Roo.bootstrap.DocumentManager = function(config){
28426     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28427     
28428     this.files = [];
28429     this.delegates = [];
28430     
28431     this.addEvents({
28432         /**
28433          * @event initial
28434          * Fire when initial the DocumentManager
28435          * @param {Roo.bootstrap.DocumentManager} this
28436          */
28437         "initial" : true,
28438         /**
28439          * @event inspect
28440          * inspect selected file
28441          * @param {Roo.bootstrap.DocumentManager} this
28442          * @param {File} file
28443          */
28444         "inspect" : true,
28445         /**
28446          * @event exception
28447          * Fire when xhr load exception
28448          * @param {Roo.bootstrap.DocumentManager} this
28449          * @param {XMLHttpRequest} xhr
28450          */
28451         "exception" : true,
28452         /**
28453          * @event afterupload
28454          * Fire when xhr load exception
28455          * @param {Roo.bootstrap.DocumentManager} this
28456          * @param {XMLHttpRequest} xhr
28457          */
28458         "afterupload" : true,
28459         /**
28460          * @event prepare
28461          * prepare the form data
28462          * @param {Roo.bootstrap.DocumentManager} this
28463          * @param {Object} formData
28464          */
28465         "prepare" : true,
28466         /**
28467          * @event remove
28468          * Fire when remove the file
28469          * @param {Roo.bootstrap.DocumentManager} this
28470          * @param {Object} file
28471          */
28472         "remove" : true,
28473         /**
28474          * @event refresh
28475          * Fire after refresh the file
28476          * @param {Roo.bootstrap.DocumentManager} this
28477          */
28478         "refresh" : true,
28479         /**
28480          * @event click
28481          * Fire after click the image
28482          * @param {Roo.bootstrap.DocumentManager} this
28483          * @param {Object} file
28484          */
28485         "click" : true,
28486         /**
28487          * @event edit
28488          * Fire when upload a image and editable set to true
28489          * @param {Roo.bootstrap.DocumentManager} this
28490          * @param {Object} file
28491          */
28492         "edit" : true,
28493         /**
28494          * @event beforeselectfile
28495          * Fire before select file
28496          * @param {Roo.bootstrap.DocumentManager} this
28497          */
28498         "beforeselectfile" : true,
28499         /**
28500          * @event process
28501          * Fire before process file
28502          * @param {Roo.bootstrap.DocumentManager} this
28503          * @param {Object} file
28504          */
28505         "process" : true,
28506         /**
28507          * @event previewrendered
28508          * Fire when preview rendered
28509          * @param {Roo.bootstrap.DocumentManager} this
28510          * @param {Object} file
28511          */
28512         "previewrendered" : true
28513         
28514     });
28515 };
28516
28517 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28518     
28519     boxes : 0,
28520     inputName : '',
28521     thumbSize : 300,
28522     multiple : true,
28523     files : false,
28524     method : 'POST',
28525     url : '',
28526     paramName : 'imageUpload',
28527     toolTipName : 'filename',
28528     fieldLabel : '',
28529     labelWidth : 4,
28530     labelAlign : 'left',
28531     editable : true,
28532     delegates : false,
28533     xhr : false, 
28534     
28535     labellg : 0,
28536     labelmd : 0,
28537     labelsm : 0,
28538     labelxs : 0,
28539     
28540     getAutoCreate : function()
28541     {   
28542         var managerWidget = {
28543             tag : 'div',
28544             cls : 'roo-document-manager',
28545             cn : [
28546                 {
28547                     tag : 'input',
28548                     cls : 'roo-document-manager-selector',
28549                     type : 'file'
28550                 },
28551                 {
28552                     tag : 'div',
28553                     cls : 'roo-document-manager-uploader',
28554                     cn : [
28555                         {
28556                             tag : 'div',
28557                             cls : 'roo-document-manager-upload-btn',
28558                             html : '<i class="fa fa-plus"></i>'
28559                         }
28560                     ]
28561                     
28562                 }
28563             ]
28564         };
28565         
28566         var content = [
28567             {
28568                 tag : 'div',
28569                 cls : 'column col-md-12',
28570                 cn : managerWidget
28571             }
28572         ];
28573         
28574         if(this.fieldLabel.length){
28575             
28576             content = [
28577                 {
28578                     tag : 'div',
28579                     cls : 'column col-md-12',
28580                     html : this.fieldLabel
28581                 },
28582                 {
28583                     tag : 'div',
28584                     cls : 'column col-md-12',
28585                     cn : managerWidget
28586                 }
28587             ];
28588
28589             if(this.labelAlign == 'left'){
28590                 content = [
28591                     {
28592                         tag : 'div',
28593                         cls : 'column',
28594                         html : this.fieldLabel
28595                     },
28596                     {
28597                         tag : 'div',
28598                         cls : 'column',
28599                         cn : managerWidget
28600                     }
28601                 ];
28602                 
28603                 if(this.labelWidth > 12){
28604                     content[0].style = "width: " + this.labelWidth + 'px';
28605                 }
28606
28607                 if(this.labelWidth < 13 && this.labelmd == 0){
28608                     this.labelmd = this.labelWidth;
28609                 }
28610
28611                 if(this.labellg > 0){
28612                     content[0].cls += ' col-lg-' + this.labellg;
28613                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28614                 }
28615
28616                 if(this.labelmd > 0){
28617                     content[0].cls += ' col-md-' + this.labelmd;
28618                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28619                 }
28620
28621                 if(this.labelsm > 0){
28622                     content[0].cls += ' col-sm-' + this.labelsm;
28623                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28624                 }
28625
28626                 if(this.labelxs > 0){
28627                     content[0].cls += ' col-xs-' + this.labelxs;
28628                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28629                 }
28630                 
28631             }
28632         }
28633         
28634         var cfg = {
28635             tag : 'div',
28636             cls : 'row clearfix',
28637             cn : content
28638         };
28639         
28640         return cfg;
28641         
28642     },
28643     
28644     initEvents : function()
28645     {
28646         this.managerEl = this.el.select('.roo-document-manager', true).first();
28647         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28648         
28649         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28650         this.selectorEl.hide();
28651         
28652         if(this.multiple){
28653             this.selectorEl.attr('multiple', 'multiple');
28654         }
28655         
28656         this.selectorEl.on('change', this.onFileSelected, this);
28657         
28658         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28659         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28660         
28661         this.uploader.on('click', this.onUploaderClick, this);
28662         
28663         this.renderProgressDialog();
28664         
28665         var _this = this;
28666         
28667         window.addEventListener("resize", function() { _this.refresh(); } );
28668         
28669         this.fireEvent('initial', this);
28670     },
28671     
28672     renderProgressDialog : function()
28673     {
28674         var _this = this;
28675         
28676         this.progressDialog = new Roo.bootstrap.Modal({
28677             cls : 'roo-document-manager-progress-dialog',
28678             allow_close : false,
28679             title : '',
28680             buttons : [
28681                 {
28682                     name  :'cancel',
28683                     weight : 'danger',
28684                     html : 'Cancel'
28685                 }
28686             ], 
28687             listeners : { 
28688                 btnclick : function() {
28689                     _this.uploadCancel();
28690                     this.hide();
28691                 }
28692             }
28693         });
28694          
28695         this.progressDialog.render(Roo.get(document.body));
28696          
28697         this.progress = new Roo.bootstrap.Progress({
28698             cls : 'roo-document-manager-progress',
28699             active : true,
28700             striped : true
28701         });
28702         
28703         this.progress.render(this.progressDialog.getChildContainer());
28704         
28705         this.progressBar = new Roo.bootstrap.ProgressBar({
28706             cls : 'roo-document-manager-progress-bar',
28707             aria_valuenow : 0,
28708             aria_valuemin : 0,
28709             aria_valuemax : 12,
28710             panel : 'success'
28711         });
28712         
28713         this.progressBar.render(this.progress.getChildContainer());
28714     },
28715     
28716     onUploaderClick : function(e)
28717     {
28718         e.preventDefault();
28719      
28720         if(this.fireEvent('beforeselectfile', this) != false){
28721             this.selectorEl.dom.click();
28722         }
28723         
28724     },
28725     
28726     onFileSelected : function(e)
28727     {
28728         e.preventDefault();
28729         
28730         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28731             return;
28732         }
28733         
28734         Roo.each(this.selectorEl.dom.files, function(file){
28735             if(this.fireEvent('inspect', this, file) != false){
28736                 this.files.push(file);
28737             }
28738         }, this);
28739         
28740         this.queue();
28741         
28742     },
28743     
28744     queue : function()
28745     {
28746         this.selectorEl.dom.value = '';
28747         
28748         if(!this.files || !this.files.length){
28749             return;
28750         }
28751         
28752         if(this.boxes > 0 && this.files.length > this.boxes){
28753             this.files = this.files.slice(0, this.boxes);
28754         }
28755         
28756         this.uploader.show();
28757         
28758         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28759             this.uploader.hide();
28760         }
28761         
28762         var _this = this;
28763         
28764         var files = [];
28765         
28766         var docs = [];
28767         
28768         Roo.each(this.files, function(file){
28769             
28770             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28771                 var f = this.renderPreview(file);
28772                 files.push(f);
28773                 return;
28774             }
28775             
28776             if(file.type.indexOf('image') != -1){
28777                 this.delegates.push(
28778                     (function(){
28779                         _this.process(file);
28780                     }).createDelegate(this)
28781                 );
28782         
28783                 return;
28784             }
28785             
28786             docs.push(
28787                 (function(){
28788                     _this.process(file);
28789                 }).createDelegate(this)
28790             );
28791             
28792         }, this);
28793         
28794         this.files = files;
28795         
28796         this.delegates = this.delegates.concat(docs);
28797         
28798         if(!this.delegates.length){
28799             this.refresh();
28800             return;
28801         }
28802         
28803         this.progressBar.aria_valuemax = this.delegates.length;
28804         
28805         this.arrange();
28806         
28807         return;
28808     },
28809     
28810     arrange : function()
28811     {
28812         if(!this.delegates.length){
28813             this.progressDialog.hide();
28814             this.refresh();
28815             return;
28816         }
28817         
28818         var delegate = this.delegates.shift();
28819         
28820         this.progressDialog.show();
28821         
28822         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28823         
28824         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28825         
28826         delegate();
28827     },
28828     
28829     refresh : function()
28830     {
28831         this.uploader.show();
28832         
28833         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28834             this.uploader.hide();
28835         }
28836         
28837         Roo.isTouch ? this.closable(false) : this.closable(true);
28838         
28839         this.fireEvent('refresh', this);
28840     },
28841     
28842     onRemove : function(e, el, o)
28843     {
28844         e.preventDefault();
28845         
28846         this.fireEvent('remove', this, o);
28847         
28848     },
28849     
28850     remove : function(o)
28851     {
28852         var files = [];
28853         
28854         Roo.each(this.files, function(file){
28855             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28856                 files.push(file);
28857                 return;
28858             }
28859
28860             o.target.remove();
28861
28862         }, this);
28863         
28864         this.files = files;
28865         
28866         this.refresh();
28867     },
28868     
28869     clear : function()
28870     {
28871         Roo.each(this.files, function(file){
28872             if(!file.target){
28873                 return;
28874             }
28875             
28876             file.target.remove();
28877
28878         }, this);
28879         
28880         this.files = [];
28881         
28882         this.refresh();
28883     },
28884     
28885     onClick : function(e, el, o)
28886     {
28887         e.preventDefault();
28888         
28889         this.fireEvent('click', this, o);
28890         
28891     },
28892     
28893     closable : function(closable)
28894     {
28895         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28896             
28897             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28898             
28899             if(closable){
28900                 el.show();
28901                 return;
28902             }
28903             
28904             el.hide();
28905             
28906         }, this);
28907     },
28908     
28909     xhrOnLoad : function(xhr)
28910     {
28911         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28912             el.remove();
28913         }, this);
28914         
28915         if (xhr.readyState !== 4) {
28916             this.arrange();
28917             this.fireEvent('exception', this, xhr);
28918             return;
28919         }
28920
28921         var response = Roo.decode(xhr.responseText);
28922         
28923         if(!response.success){
28924             this.arrange();
28925             this.fireEvent('exception', this, xhr);
28926             return;
28927         }
28928         
28929         var file = this.renderPreview(response.data);
28930         
28931         this.files.push(file);
28932         
28933         this.arrange();
28934         
28935         this.fireEvent('afterupload', this, xhr);
28936         
28937     },
28938     
28939     xhrOnError : function(xhr)
28940     {
28941         Roo.log('xhr on error');
28942         
28943         var response = Roo.decode(xhr.responseText);
28944           
28945         Roo.log(response);
28946         
28947         this.arrange();
28948     },
28949     
28950     process : function(file)
28951     {
28952         if(this.fireEvent('process', this, file) !== false){
28953             if(this.editable && file.type.indexOf('image') != -1){
28954                 this.fireEvent('edit', this, file);
28955                 return;
28956             }
28957
28958             this.uploadStart(file, false);
28959
28960             return;
28961         }
28962         
28963     },
28964     
28965     uploadStart : function(file, crop)
28966     {
28967         this.xhr = new XMLHttpRequest();
28968         
28969         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28970             this.arrange();
28971             return;
28972         }
28973         
28974         file.xhr = this.xhr;
28975             
28976         this.managerEl.createChild({
28977             tag : 'div',
28978             cls : 'roo-document-manager-loading',
28979             cn : [
28980                 {
28981                     tag : 'div',
28982                     tooltip : file.name,
28983                     cls : 'roo-document-manager-thumb',
28984                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28985                 }
28986             ]
28987
28988         });
28989
28990         this.xhr.open(this.method, this.url, true);
28991         
28992         var headers = {
28993             "Accept": "application/json",
28994             "Cache-Control": "no-cache",
28995             "X-Requested-With": "XMLHttpRequest"
28996         };
28997         
28998         for (var headerName in headers) {
28999             var headerValue = headers[headerName];
29000             if (headerValue) {
29001                 this.xhr.setRequestHeader(headerName, headerValue);
29002             }
29003         }
29004         
29005         var _this = this;
29006         
29007         this.xhr.onload = function()
29008         {
29009             _this.xhrOnLoad(_this.xhr);
29010         }
29011         
29012         this.xhr.onerror = function()
29013         {
29014             _this.xhrOnError(_this.xhr);
29015         }
29016         
29017         var formData = new FormData();
29018
29019         formData.append('returnHTML', 'NO');
29020         
29021         if(crop){
29022             formData.append('crop', crop);
29023         }
29024         
29025         formData.append(this.paramName, file, file.name);
29026         
29027         var options = {
29028             file : file, 
29029             manually : false
29030         };
29031         
29032         if(this.fireEvent('prepare', this, formData, options) != false){
29033             
29034             if(options.manually){
29035                 return;
29036             }
29037             
29038             this.xhr.send(formData);
29039             return;
29040         };
29041         
29042         this.uploadCancel();
29043     },
29044     
29045     uploadCancel : function()
29046     {
29047         if (this.xhr) {
29048             this.xhr.abort();
29049         }
29050         
29051         this.delegates = [];
29052         
29053         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29054             el.remove();
29055         }, this);
29056         
29057         this.arrange();
29058     },
29059     
29060     renderPreview : function(file)
29061     {
29062         if(typeof(file.target) != 'undefined' && file.target){
29063             return file;
29064         }
29065         
29066         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29067         
29068         var previewEl = this.managerEl.createChild({
29069             tag : 'div',
29070             cls : 'roo-document-manager-preview',
29071             cn : [
29072                 {
29073                     tag : 'div',
29074                     tooltip : file[this.toolTipName],
29075                     cls : 'roo-document-manager-thumb',
29076                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29077                 },
29078                 {
29079                     tag : 'button',
29080                     cls : 'close',
29081                     html : '<i class="fa fa-times-circle"></i>'
29082                 }
29083             ]
29084         });
29085
29086         var close = previewEl.select('button.close', true).first();
29087
29088         close.on('click', this.onRemove, this, file);
29089
29090         file.target = previewEl;
29091
29092         var image = previewEl.select('img', true).first();
29093         
29094         var _this = this;
29095         
29096         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29097         
29098         image.on('click', this.onClick, this, file);
29099         
29100         this.fireEvent('previewrendered', this, file);
29101         
29102         return file;
29103         
29104     },
29105     
29106     onPreviewLoad : function(file, image)
29107     {
29108         if(typeof(file.target) == 'undefined' || !file.target){
29109             return;
29110         }
29111         
29112         var width = image.dom.naturalWidth || image.dom.width;
29113         var height = image.dom.naturalHeight || image.dom.height;
29114         
29115         if(width > height){
29116             file.target.addClass('wide');
29117             return;
29118         }
29119         
29120         file.target.addClass('tall');
29121         return;
29122         
29123     },
29124     
29125     uploadFromSource : function(file, crop)
29126     {
29127         this.xhr = new XMLHttpRequest();
29128         
29129         this.managerEl.createChild({
29130             tag : 'div',
29131             cls : 'roo-document-manager-loading',
29132             cn : [
29133                 {
29134                     tag : 'div',
29135                     tooltip : file.name,
29136                     cls : 'roo-document-manager-thumb',
29137                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29138                 }
29139             ]
29140
29141         });
29142
29143         this.xhr.open(this.method, this.url, true);
29144         
29145         var headers = {
29146             "Accept": "application/json",
29147             "Cache-Control": "no-cache",
29148             "X-Requested-With": "XMLHttpRequest"
29149         };
29150         
29151         for (var headerName in headers) {
29152             var headerValue = headers[headerName];
29153             if (headerValue) {
29154                 this.xhr.setRequestHeader(headerName, headerValue);
29155             }
29156         }
29157         
29158         var _this = this;
29159         
29160         this.xhr.onload = function()
29161         {
29162             _this.xhrOnLoad(_this.xhr);
29163         }
29164         
29165         this.xhr.onerror = function()
29166         {
29167             _this.xhrOnError(_this.xhr);
29168         }
29169         
29170         var formData = new FormData();
29171
29172         formData.append('returnHTML', 'NO');
29173         
29174         formData.append('crop', crop);
29175         
29176         if(typeof(file.filename) != 'undefined'){
29177             formData.append('filename', file.filename);
29178         }
29179         
29180         if(typeof(file.mimetype) != 'undefined'){
29181             formData.append('mimetype', file.mimetype);
29182         }
29183         
29184         Roo.log(formData);
29185         
29186         if(this.fireEvent('prepare', this, formData) != false){
29187             this.xhr.send(formData);
29188         };
29189     }
29190 });
29191
29192 /*
29193 * Licence: LGPL
29194 */
29195
29196 /**
29197  * @class Roo.bootstrap.DocumentViewer
29198  * @extends Roo.bootstrap.Component
29199  * Bootstrap DocumentViewer class
29200  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29201  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29202  * 
29203  * @constructor
29204  * Create a new DocumentViewer
29205  * @param {Object} config The config object
29206  */
29207
29208 Roo.bootstrap.DocumentViewer = function(config){
29209     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29210     
29211     this.addEvents({
29212         /**
29213          * @event initial
29214          * Fire after initEvent
29215          * @param {Roo.bootstrap.DocumentViewer} this
29216          */
29217         "initial" : true,
29218         /**
29219          * @event click
29220          * Fire after click
29221          * @param {Roo.bootstrap.DocumentViewer} this
29222          */
29223         "click" : true,
29224         /**
29225          * @event download
29226          * Fire after download button
29227          * @param {Roo.bootstrap.DocumentViewer} this
29228          */
29229         "download" : true,
29230         /**
29231          * @event trash
29232          * Fire after trash button
29233          * @param {Roo.bootstrap.DocumentViewer} this
29234          */
29235         "trash" : true
29236         
29237     });
29238 };
29239
29240 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29241     
29242     showDownload : true,
29243     
29244     showTrash : true,
29245     
29246     getAutoCreate : function()
29247     {
29248         var cfg = {
29249             tag : 'div',
29250             cls : 'roo-document-viewer',
29251             cn : [
29252                 {
29253                     tag : 'div',
29254                     cls : 'roo-document-viewer-body',
29255                     cn : [
29256                         {
29257                             tag : 'div',
29258                             cls : 'roo-document-viewer-thumb',
29259                             cn : [
29260                                 {
29261                                     tag : 'img',
29262                                     cls : 'roo-document-viewer-image'
29263                                 }
29264                             ]
29265                         }
29266                     ]
29267                 },
29268                 {
29269                     tag : 'div',
29270                     cls : 'roo-document-viewer-footer',
29271                     cn : {
29272                         tag : 'div',
29273                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29274                         cn : [
29275                             {
29276                                 tag : 'div',
29277                                 cls : 'btn-group roo-document-viewer-download',
29278                                 cn : [
29279                                     {
29280                                         tag : 'button',
29281                                         cls : 'btn btn-default',
29282                                         html : '<i class="fa fa-download"></i>'
29283                                     }
29284                                 ]
29285                             },
29286                             {
29287                                 tag : 'div',
29288                                 cls : 'btn-group roo-document-viewer-trash',
29289                                 cn : [
29290                                     {
29291                                         tag : 'button',
29292                                         cls : 'btn btn-default',
29293                                         html : '<i class="fa fa-trash"></i>'
29294                                     }
29295                                 ]
29296                             }
29297                         ]
29298                     }
29299                 }
29300             ]
29301         };
29302         
29303         return cfg;
29304     },
29305     
29306     initEvents : function()
29307     {
29308         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29309         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29310         
29311         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29312         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29313         
29314         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29315         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29316         
29317         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29318         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29319         
29320         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29321         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29322         
29323         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29324         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29325         
29326         this.bodyEl.on('click', this.onClick, this);
29327         this.downloadBtn.on('click', this.onDownload, this);
29328         this.trashBtn.on('click', this.onTrash, this);
29329         
29330         this.downloadBtn.hide();
29331         this.trashBtn.hide();
29332         
29333         if(this.showDownload){
29334             this.downloadBtn.show();
29335         }
29336         
29337         if(this.showTrash){
29338             this.trashBtn.show();
29339         }
29340         
29341         if(!this.showDownload && !this.showTrash) {
29342             this.footerEl.hide();
29343         }
29344         
29345     },
29346     
29347     initial : function()
29348     {
29349         this.fireEvent('initial', this);
29350         
29351     },
29352     
29353     onClick : function(e)
29354     {
29355         e.preventDefault();
29356         
29357         this.fireEvent('click', this);
29358     },
29359     
29360     onDownload : function(e)
29361     {
29362         e.preventDefault();
29363         
29364         this.fireEvent('download', this);
29365     },
29366     
29367     onTrash : function(e)
29368     {
29369         e.preventDefault();
29370         
29371         this.fireEvent('trash', this);
29372     }
29373     
29374 });
29375 /*
29376  * - LGPL
29377  *
29378  * nav progress bar
29379  * 
29380  */
29381
29382 /**
29383  * @class Roo.bootstrap.NavProgressBar
29384  * @extends Roo.bootstrap.Component
29385  * Bootstrap NavProgressBar class
29386  * 
29387  * @constructor
29388  * Create a new nav progress bar
29389  * @param {Object} config The config object
29390  */
29391
29392 Roo.bootstrap.NavProgressBar = function(config){
29393     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29394
29395     this.bullets = this.bullets || [];
29396    
29397 //    Roo.bootstrap.NavProgressBar.register(this);
29398      this.addEvents({
29399         /**
29400              * @event changed
29401              * Fires when the active item changes
29402              * @param {Roo.bootstrap.NavProgressBar} this
29403              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29404              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29405          */
29406         'changed': true
29407      });
29408     
29409 };
29410
29411 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29412     
29413     bullets : [],
29414     barItems : [],
29415     
29416     getAutoCreate : function()
29417     {
29418         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29419         
29420         cfg = {
29421             tag : 'div',
29422             cls : 'roo-navigation-bar-group',
29423             cn : [
29424                 {
29425                     tag : 'div',
29426                     cls : 'roo-navigation-top-bar'
29427                 },
29428                 {
29429                     tag : 'div',
29430                     cls : 'roo-navigation-bullets-bar',
29431                     cn : [
29432                         {
29433                             tag : 'ul',
29434                             cls : 'roo-navigation-bar'
29435                         }
29436                     ]
29437                 },
29438                 
29439                 {
29440                     tag : 'div',
29441                     cls : 'roo-navigation-bottom-bar'
29442                 }
29443             ]
29444             
29445         };
29446         
29447         return cfg;
29448         
29449     },
29450     
29451     initEvents: function() 
29452     {
29453         
29454     },
29455     
29456     onRender : function(ct, position) 
29457     {
29458         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29459         
29460         if(this.bullets.length){
29461             Roo.each(this.bullets, function(b){
29462                this.addItem(b);
29463             }, this);
29464         }
29465         
29466         this.format();
29467         
29468     },
29469     
29470     addItem : function(cfg)
29471     {
29472         var item = new Roo.bootstrap.NavProgressItem(cfg);
29473         
29474         item.parentId = this.id;
29475         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29476         
29477         if(cfg.html){
29478             var top = new Roo.bootstrap.Element({
29479                 tag : 'div',
29480                 cls : 'roo-navigation-bar-text'
29481             });
29482             
29483             var bottom = new Roo.bootstrap.Element({
29484                 tag : 'div',
29485                 cls : 'roo-navigation-bar-text'
29486             });
29487             
29488             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29489             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29490             
29491             var topText = new Roo.bootstrap.Element({
29492                 tag : 'span',
29493                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29494             });
29495             
29496             var bottomText = new Roo.bootstrap.Element({
29497                 tag : 'span',
29498                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29499             });
29500             
29501             topText.onRender(top.el, null);
29502             bottomText.onRender(bottom.el, null);
29503             
29504             item.topEl = top;
29505             item.bottomEl = bottom;
29506         }
29507         
29508         this.barItems.push(item);
29509         
29510         return item;
29511     },
29512     
29513     getActive : function()
29514     {
29515         var active = false;
29516         
29517         Roo.each(this.barItems, function(v){
29518             
29519             if (!v.isActive()) {
29520                 return;
29521             }
29522             
29523             active = v;
29524             return false;
29525             
29526         });
29527         
29528         return active;
29529     },
29530     
29531     setActiveItem : function(item)
29532     {
29533         var prev = false;
29534         
29535         Roo.each(this.barItems, function(v){
29536             if (v.rid == item.rid) {
29537                 return ;
29538             }
29539             
29540             if (v.isActive()) {
29541                 v.setActive(false);
29542                 prev = v;
29543             }
29544         });
29545
29546         item.setActive(true);
29547         
29548         this.fireEvent('changed', this, item, prev);
29549     },
29550     
29551     getBarItem: function(rid)
29552     {
29553         var ret = false;
29554         
29555         Roo.each(this.barItems, function(e) {
29556             if (e.rid != rid) {
29557                 return;
29558             }
29559             
29560             ret =  e;
29561             return false;
29562         });
29563         
29564         return ret;
29565     },
29566     
29567     indexOfItem : function(item)
29568     {
29569         var index = false;
29570         
29571         Roo.each(this.barItems, function(v, i){
29572             
29573             if (v.rid != item.rid) {
29574                 return;
29575             }
29576             
29577             index = i;
29578             return false
29579         });
29580         
29581         return index;
29582     },
29583     
29584     setActiveNext : function()
29585     {
29586         var i = this.indexOfItem(this.getActive());
29587         
29588         if (i > this.barItems.length) {
29589             return;
29590         }
29591         
29592         this.setActiveItem(this.barItems[i+1]);
29593     },
29594     
29595     setActivePrev : function()
29596     {
29597         var i = this.indexOfItem(this.getActive());
29598         
29599         if (i  < 1) {
29600             return;
29601         }
29602         
29603         this.setActiveItem(this.barItems[i-1]);
29604     },
29605     
29606     format : function()
29607     {
29608         if(!this.barItems.length){
29609             return;
29610         }
29611      
29612         var width = 100 / this.barItems.length;
29613         
29614         Roo.each(this.barItems, function(i){
29615             i.el.setStyle('width', width + '%');
29616             i.topEl.el.setStyle('width', width + '%');
29617             i.bottomEl.el.setStyle('width', width + '%');
29618         }, this);
29619         
29620     }
29621     
29622 });
29623 /*
29624  * - LGPL
29625  *
29626  * Nav Progress Item
29627  * 
29628  */
29629
29630 /**
29631  * @class Roo.bootstrap.NavProgressItem
29632  * @extends Roo.bootstrap.Component
29633  * Bootstrap NavProgressItem class
29634  * @cfg {String} rid the reference id
29635  * @cfg {Boolean} active (true|false) Is item active default false
29636  * @cfg {Boolean} disabled (true|false) Is item active default false
29637  * @cfg {String} html
29638  * @cfg {String} position (top|bottom) text position default bottom
29639  * @cfg {String} icon show icon instead of number
29640  * 
29641  * @constructor
29642  * Create a new NavProgressItem
29643  * @param {Object} config The config object
29644  */
29645 Roo.bootstrap.NavProgressItem = function(config){
29646     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29647     this.addEvents({
29648         // raw events
29649         /**
29650          * @event click
29651          * The raw click event for the entire grid.
29652          * @param {Roo.bootstrap.NavProgressItem} this
29653          * @param {Roo.EventObject} e
29654          */
29655         "click" : true
29656     });
29657    
29658 };
29659
29660 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29661     
29662     rid : '',
29663     active : false,
29664     disabled : false,
29665     html : '',
29666     position : 'bottom',
29667     icon : false,
29668     
29669     getAutoCreate : function()
29670     {
29671         var iconCls = 'roo-navigation-bar-item-icon';
29672         
29673         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29674         
29675         var cfg = {
29676             tag: 'li',
29677             cls: 'roo-navigation-bar-item',
29678             cn : [
29679                 {
29680                     tag : 'i',
29681                     cls : iconCls
29682                 }
29683             ]
29684         };
29685         
29686         if(this.active){
29687             cfg.cls += ' active';
29688         }
29689         if(this.disabled){
29690             cfg.cls += ' disabled';
29691         }
29692         
29693         return cfg;
29694     },
29695     
29696     disable : function()
29697     {
29698         this.setDisabled(true);
29699     },
29700     
29701     enable : function()
29702     {
29703         this.setDisabled(false);
29704     },
29705     
29706     initEvents: function() 
29707     {
29708         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29709         
29710         this.iconEl.on('click', this.onClick, this);
29711     },
29712     
29713     onClick : function(e)
29714     {
29715         e.preventDefault();
29716         
29717         if(this.disabled){
29718             return;
29719         }
29720         
29721         if(this.fireEvent('click', this, e) === false){
29722             return;
29723         };
29724         
29725         this.parent().setActiveItem(this);
29726     },
29727     
29728     isActive: function () 
29729     {
29730         return this.active;
29731     },
29732     
29733     setActive : function(state)
29734     {
29735         if(this.active == state){
29736             return;
29737         }
29738         
29739         this.active = state;
29740         
29741         if (state) {
29742             this.el.addClass('active');
29743             return;
29744         }
29745         
29746         this.el.removeClass('active');
29747         
29748         return;
29749     },
29750     
29751     setDisabled : function(state)
29752     {
29753         if(this.disabled == state){
29754             return;
29755         }
29756         
29757         this.disabled = state;
29758         
29759         if (state) {
29760             this.el.addClass('disabled');
29761             return;
29762         }
29763         
29764         this.el.removeClass('disabled');
29765     },
29766     
29767     tooltipEl : function()
29768     {
29769         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29770     }
29771 });
29772  
29773
29774  /*
29775  * - LGPL
29776  *
29777  * FieldLabel
29778  * 
29779  */
29780
29781 /**
29782  * @class Roo.bootstrap.FieldLabel
29783  * @extends Roo.bootstrap.Component
29784  * Bootstrap FieldLabel class
29785  * @cfg {String} html contents of the element
29786  * @cfg {String} tag tag of the element default label
29787  * @cfg {String} cls class of the element
29788  * @cfg {String} target label target 
29789  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29790  * @cfg {String} invalidClass default "text-warning"
29791  * @cfg {String} validClass default "text-success"
29792  * @cfg {String} iconTooltip default "This field is required"
29793  * @cfg {String} indicatorpos (left|right) default left
29794  * 
29795  * @constructor
29796  * Create a new FieldLabel
29797  * @param {Object} config The config object
29798  */
29799
29800 Roo.bootstrap.FieldLabel = function(config){
29801     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29802     
29803     this.addEvents({
29804             /**
29805              * @event invalid
29806              * Fires after the field has been marked as invalid.
29807              * @param {Roo.form.FieldLabel} this
29808              * @param {String} msg The validation message
29809              */
29810             invalid : true,
29811             /**
29812              * @event valid
29813              * Fires after the field has been validated with no errors.
29814              * @param {Roo.form.FieldLabel} this
29815              */
29816             valid : true
29817         });
29818 };
29819
29820 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29821     
29822     tag: 'label',
29823     cls: '',
29824     html: '',
29825     target: '',
29826     allowBlank : true,
29827     invalidClass : 'has-warning',
29828     validClass : 'has-success',
29829     iconTooltip : 'This field is required',
29830     indicatorpos : 'left',
29831     
29832     getAutoCreate : function(){
29833         
29834         var cfg = {
29835             tag : this.tag,
29836             cls : 'roo-bootstrap-field-label ' + this.cls,
29837             for : this.target,
29838             cn : [
29839                 {
29840                     tag : 'i',
29841                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29842                     tooltip : this.iconTooltip
29843                 },
29844                 {
29845                     tag : 'span',
29846                     html : this.html
29847                 }
29848             ] 
29849         };
29850         
29851         if(this.indicatorpos == 'right'){
29852             var cfg = {
29853                 tag : this.tag,
29854                 cls : 'roo-bootstrap-field-label ' + this.cls,
29855                 for : this.target,
29856                 cn : [
29857                     {
29858                         tag : 'span',
29859                         html : this.html
29860                     },
29861                     {
29862                         tag : 'i',
29863                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29864                         tooltip : this.iconTooltip
29865                     }
29866                 ] 
29867             };
29868         }
29869         
29870         return cfg;
29871     },
29872     
29873     initEvents: function() 
29874     {
29875         Roo.bootstrap.Element.superclass.initEvents.call(this);
29876         
29877         this.indicator = this.indicatorEl();
29878         
29879         if(this.indicator){
29880             this.indicator.removeClass('visible');
29881             this.indicator.addClass('invisible');
29882         }
29883         
29884         Roo.bootstrap.FieldLabel.register(this);
29885     },
29886     
29887     indicatorEl : function()
29888     {
29889         var indicator = this.el.select('i.roo-required-indicator',true).first();
29890         
29891         if(!indicator){
29892             return false;
29893         }
29894         
29895         return indicator;
29896         
29897     },
29898     
29899     /**
29900      * Mark this field as valid
29901      */
29902     markValid : function()
29903     {
29904         if(this.indicator){
29905             this.indicator.removeClass('visible');
29906             this.indicator.addClass('invisible');
29907         }
29908         
29909         this.el.removeClass(this.invalidClass);
29910         
29911         this.el.addClass(this.validClass);
29912         
29913         this.fireEvent('valid', this);
29914     },
29915     
29916     /**
29917      * Mark this field as invalid
29918      * @param {String} msg The validation message
29919      */
29920     markInvalid : function(msg)
29921     {
29922         if(this.indicator){
29923             this.indicator.removeClass('invisible');
29924             this.indicator.addClass('visible');
29925         }
29926         
29927         this.el.removeClass(this.validClass);
29928         
29929         this.el.addClass(this.invalidClass);
29930         
29931         this.fireEvent('invalid', this, msg);
29932     }
29933     
29934    
29935 });
29936
29937 Roo.apply(Roo.bootstrap.FieldLabel, {
29938     
29939     groups: {},
29940     
29941      /**
29942     * register a FieldLabel Group
29943     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29944     */
29945     register : function(label)
29946     {
29947         if(this.groups.hasOwnProperty(label.target)){
29948             return;
29949         }
29950      
29951         this.groups[label.target] = label;
29952         
29953     },
29954     /**
29955     * fetch a FieldLabel Group based on the target
29956     * @param {string} target
29957     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29958     */
29959     get: function(target) {
29960         if (typeof(this.groups[target]) == 'undefined') {
29961             return false;
29962         }
29963         
29964         return this.groups[target] ;
29965     }
29966 });
29967
29968  
29969
29970  /*
29971  * - LGPL
29972  *
29973  * page DateSplitField.
29974  * 
29975  */
29976
29977
29978 /**
29979  * @class Roo.bootstrap.DateSplitField
29980  * @extends Roo.bootstrap.Component
29981  * Bootstrap DateSplitField class
29982  * @cfg {string} fieldLabel - the label associated
29983  * @cfg {Number} labelWidth set the width of label (0-12)
29984  * @cfg {String} labelAlign (top|left)
29985  * @cfg {Boolean} dayAllowBlank (true|false) default false
29986  * @cfg {Boolean} monthAllowBlank (true|false) default false
29987  * @cfg {Boolean} yearAllowBlank (true|false) default false
29988  * @cfg {string} dayPlaceholder 
29989  * @cfg {string} monthPlaceholder
29990  * @cfg {string} yearPlaceholder
29991  * @cfg {string} dayFormat default 'd'
29992  * @cfg {string} monthFormat default 'm'
29993  * @cfg {string} yearFormat default 'Y'
29994  * @cfg {Number} labellg set the width of label (1-12)
29995  * @cfg {Number} labelmd set the width of label (1-12)
29996  * @cfg {Number} labelsm set the width of label (1-12)
29997  * @cfg {Number} labelxs set the width of label (1-12)
29998
29999  *     
30000  * @constructor
30001  * Create a new DateSplitField
30002  * @param {Object} config The config object
30003  */
30004
30005 Roo.bootstrap.DateSplitField = function(config){
30006     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30007     
30008     this.addEvents({
30009         // raw events
30010          /**
30011          * @event years
30012          * getting the data of years
30013          * @param {Roo.bootstrap.DateSplitField} this
30014          * @param {Object} years
30015          */
30016         "years" : true,
30017         /**
30018          * @event days
30019          * getting the data of days
30020          * @param {Roo.bootstrap.DateSplitField} this
30021          * @param {Object} days
30022          */
30023         "days" : true,
30024         /**
30025          * @event invalid
30026          * Fires after the field has been marked as invalid.
30027          * @param {Roo.form.Field} this
30028          * @param {String} msg The validation message
30029          */
30030         invalid : true,
30031        /**
30032          * @event valid
30033          * Fires after the field has been validated with no errors.
30034          * @param {Roo.form.Field} this
30035          */
30036         valid : true
30037     });
30038 };
30039
30040 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30041     
30042     fieldLabel : '',
30043     labelAlign : 'top',
30044     labelWidth : 3,
30045     dayAllowBlank : false,
30046     monthAllowBlank : false,
30047     yearAllowBlank : false,
30048     dayPlaceholder : '',
30049     monthPlaceholder : '',
30050     yearPlaceholder : '',
30051     dayFormat : 'd',
30052     monthFormat : 'm',
30053     yearFormat : 'Y',
30054     isFormField : true,
30055     labellg : 0,
30056     labelmd : 0,
30057     labelsm : 0,
30058     labelxs : 0,
30059     
30060     getAutoCreate : function()
30061     {
30062         var cfg = {
30063             tag : 'div',
30064             cls : 'row roo-date-split-field-group',
30065             cn : [
30066                 {
30067                     tag : 'input',
30068                     type : 'hidden',
30069                     cls : 'form-hidden-field roo-date-split-field-group-value',
30070                     name : this.name
30071                 }
30072             ]
30073         };
30074         
30075         var labelCls = 'col-md-12';
30076         var contentCls = 'col-md-4';
30077         
30078         if(this.fieldLabel){
30079             
30080             var label = {
30081                 tag : 'div',
30082                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30083                 cn : [
30084                     {
30085                         tag : 'label',
30086                         html : this.fieldLabel
30087                     }
30088                 ]
30089             };
30090             
30091             if(this.labelAlign == 'left'){
30092             
30093                 if(this.labelWidth > 12){
30094                     label.style = "width: " + this.labelWidth + 'px';
30095                 }
30096
30097                 if(this.labelWidth < 13 && this.labelmd == 0){
30098                     this.labelmd = this.labelWidth;
30099                 }
30100
30101                 if(this.labellg > 0){
30102                     labelCls = ' col-lg-' + this.labellg;
30103                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30104                 }
30105
30106                 if(this.labelmd > 0){
30107                     labelCls = ' col-md-' + this.labelmd;
30108                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30109                 }
30110
30111                 if(this.labelsm > 0){
30112                     labelCls = ' col-sm-' + this.labelsm;
30113                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30114                 }
30115
30116                 if(this.labelxs > 0){
30117                     labelCls = ' col-xs-' + this.labelxs;
30118                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30119                 }
30120             }
30121             
30122             label.cls += ' ' + labelCls;
30123             
30124             cfg.cn.push(label);
30125         }
30126         
30127         Roo.each(['day', 'month', 'year'], function(t){
30128             cfg.cn.push({
30129                 tag : 'div',
30130                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30131             });
30132         }, this);
30133         
30134         return cfg;
30135     },
30136     
30137     inputEl: function ()
30138     {
30139         return this.el.select('.roo-date-split-field-group-value', true).first();
30140     },
30141     
30142     onRender : function(ct, position) 
30143     {
30144         var _this = this;
30145         
30146         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30147         
30148         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30149         
30150         this.dayField = new Roo.bootstrap.ComboBox({
30151             allowBlank : this.dayAllowBlank,
30152             alwaysQuery : true,
30153             displayField : 'value',
30154             editable : false,
30155             fieldLabel : '',
30156             forceSelection : true,
30157             mode : 'local',
30158             placeholder : this.dayPlaceholder,
30159             selectOnFocus : true,
30160             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30161             triggerAction : 'all',
30162             typeAhead : true,
30163             valueField : 'value',
30164             store : new Roo.data.SimpleStore({
30165                 data : (function() {    
30166                     var days = [];
30167                     _this.fireEvent('days', _this, days);
30168                     return days;
30169                 })(),
30170                 fields : [ 'value' ]
30171             }),
30172             listeners : {
30173                 select : function (_self, record, index)
30174                 {
30175                     _this.setValue(_this.getValue());
30176                 }
30177             }
30178         });
30179
30180         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30181         
30182         this.monthField = new Roo.bootstrap.MonthField({
30183             after : '<i class=\"fa fa-calendar\"></i>',
30184             allowBlank : this.monthAllowBlank,
30185             placeholder : this.monthPlaceholder,
30186             readOnly : true,
30187             listeners : {
30188                 render : function (_self)
30189                 {
30190                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30191                         e.preventDefault();
30192                         _self.focus();
30193                     });
30194                 },
30195                 select : function (_self, oldvalue, newvalue)
30196                 {
30197                     _this.setValue(_this.getValue());
30198                 }
30199             }
30200         });
30201         
30202         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30203         
30204         this.yearField = new Roo.bootstrap.ComboBox({
30205             allowBlank : this.yearAllowBlank,
30206             alwaysQuery : true,
30207             displayField : 'value',
30208             editable : false,
30209             fieldLabel : '',
30210             forceSelection : true,
30211             mode : 'local',
30212             placeholder : this.yearPlaceholder,
30213             selectOnFocus : true,
30214             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30215             triggerAction : 'all',
30216             typeAhead : true,
30217             valueField : 'value',
30218             store : new Roo.data.SimpleStore({
30219                 data : (function() {
30220                     var years = [];
30221                     _this.fireEvent('years', _this, years);
30222                     return years;
30223                 })(),
30224                 fields : [ 'value' ]
30225             }),
30226             listeners : {
30227                 select : function (_self, record, index)
30228                 {
30229                     _this.setValue(_this.getValue());
30230                 }
30231             }
30232         });
30233
30234         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30235     },
30236     
30237     setValue : function(v, format)
30238     {
30239         this.inputEl.dom.value = v;
30240         
30241         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30242         
30243         var d = Date.parseDate(v, f);
30244         
30245         if(!d){
30246             this.validate();
30247             return;
30248         }
30249         
30250         this.setDay(d.format(this.dayFormat));
30251         this.setMonth(d.format(this.monthFormat));
30252         this.setYear(d.format(this.yearFormat));
30253         
30254         this.validate();
30255         
30256         return;
30257     },
30258     
30259     setDay : function(v)
30260     {
30261         this.dayField.setValue(v);
30262         this.inputEl.dom.value = this.getValue();
30263         this.validate();
30264         return;
30265     },
30266     
30267     setMonth : function(v)
30268     {
30269         this.monthField.setValue(v, true);
30270         this.inputEl.dom.value = this.getValue();
30271         this.validate();
30272         return;
30273     },
30274     
30275     setYear : function(v)
30276     {
30277         this.yearField.setValue(v);
30278         this.inputEl.dom.value = this.getValue();
30279         this.validate();
30280         return;
30281     },
30282     
30283     getDay : function()
30284     {
30285         return this.dayField.getValue();
30286     },
30287     
30288     getMonth : function()
30289     {
30290         return this.monthField.getValue();
30291     },
30292     
30293     getYear : function()
30294     {
30295         return this.yearField.getValue();
30296     },
30297     
30298     getValue : function()
30299     {
30300         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30301         
30302         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30303         
30304         return date;
30305     },
30306     
30307     reset : function()
30308     {
30309         this.setDay('');
30310         this.setMonth('');
30311         this.setYear('');
30312         this.inputEl.dom.value = '';
30313         this.validate();
30314         return;
30315     },
30316     
30317     validate : function()
30318     {
30319         var d = this.dayField.validate();
30320         var m = this.monthField.validate();
30321         var y = this.yearField.validate();
30322         
30323         var valid = true;
30324         
30325         if(
30326                 (!this.dayAllowBlank && !d) ||
30327                 (!this.monthAllowBlank && !m) ||
30328                 (!this.yearAllowBlank && !y)
30329         ){
30330             valid = false;
30331         }
30332         
30333         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30334             return valid;
30335         }
30336         
30337         if(valid){
30338             this.markValid();
30339             return valid;
30340         }
30341         
30342         this.markInvalid();
30343         
30344         return valid;
30345     },
30346     
30347     markValid : function()
30348     {
30349         
30350         var label = this.el.select('label', true).first();
30351         var icon = this.el.select('i.fa-star', true).first();
30352
30353         if(label && icon){
30354             icon.remove();
30355         }
30356         
30357         this.fireEvent('valid', this);
30358     },
30359     
30360      /**
30361      * Mark this field as invalid
30362      * @param {String} msg The validation message
30363      */
30364     markInvalid : function(msg)
30365     {
30366         
30367         var label = this.el.select('label', true).first();
30368         var icon = this.el.select('i.fa-star', true).first();
30369
30370         if(label && !icon){
30371             this.el.select('.roo-date-split-field-label', true).createChild({
30372                 tag : 'i',
30373                 cls : 'text-danger fa fa-lg fa-star',
30374                 tooltip : 'This field is required',
30375                 style : 'margin-right:5px;'
30376             }, label, true);
30377         }
30378         
30379         this.fireEvent('invalid', this, msg);
30380     },
30381     
30382     clearInvalid : function()
30383     {
30384         var label = this.el.select('label', true).first();
30385         var icon = this.el.select('i.fa-star', true).first();
30386
30387         if(label && icon){
30388             icon.remove();
30389         }
30390         
30391         this.fireEvent('valid', this);
30392     },
30393     
30394     getName: function()
30395     {
30396         return this.name;
30397     }
30398     
30399 });
30400
30401  /**
30402  *
30403  * This is based on 
30404  * http://masonry.desandro.com
30405  *
30406  * The idea is to render all the bricks based on vertical width...
30407  *
30408  * The original code extends 'outlayer' - we might need to use that....
30409  * 
30410  */
30411
30412
30413 /**
30414  * @class Roo.bootstrap.LayoutMasonry
30415  * @extends Roo.bootstrap.Component
30416  * Bootstrap Layout Masonry class
30417  * 
30418  * @constructor
30419  * Create a new Element
30420  * @param {Object} config The config object
30421  */
30422
30423 Roo.bootstrap.LayoutMasonry = function(config){
30424     
30425     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30426     
30427     this.bricks = [];
30428     
30429     Roo.bootstrap.LayoutMasonry.register(this);
30430     
30431     this.addEvents({
30432         // raw events
30433         /**
30434          * @event layout
30435          * Fire after layout the items
30436          * @param {Roo.bootstrap.LayoutMasonry} this
30437          * @param {Roo.EventObject} e
30438          */
30439         "layout" : true
30440     });
30441     
30442 };
30443
30444 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30445     
30446     /**
30447      * @cfg {Boolean} isLayoutInstant = no animation?
30448      */   
30449     isLayoutInstant : false, // needed?
30450    
30451     /**
30452      * @cfg {Number} boxWidth  width of the columns
30453      */   
30454     boxWidth : 450,
30455     
30456       /**
30457      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30458      */   
30459     boxHeight : 0,
30460     
30461     /**
30462      * @cfg {Number} padWidth padding below box..
30463      */   
30464     padWidth : 10, 
30465     
30466     /**
30467      * @cfg {Number} gutter gutter width..
30468      */   
30469     gutter : 10,
30470     
30471      /**
30472      * @cfg {Number} maxCols maximum number of columns
30473      */   
30474     
30475     maxCols: 0,
30476     
30477     /**
30478      * @cfg {Boolean} isAutoInitial defalut true
30479      */   
30480     isAutoInitial : true, 
30481     
30482     containerWidth: 0,
30483     
30484     /**
30485      * @cfg {Boolean} isHorizontal defalut false
30486      */   
30487     isHorizontal : false, 
30488
30489     currentSize : null,
30490     
30491     tag: 'div',
30492     
30493     cls: '',
30494     
30495     bricks: null, //CompositeElement
30496     
30497     cols : 1,
30498     
30499     _isLayoutInited : false,
30500     
30501 //    isAlternative : false, // only use for vertical layout...
30502     
30503     /**
30504      * @cfg {Number} alternativePadWidth padding below box..
30505      */   
30506     alternativePadWidth : 50,
30507     
30508     selectedBrick : [],
30509     
30510     getAutoCreate : function(){
30511         
30512         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30513         
30514         var cfg = {
30515             tag: this.tag,
30516             cls: 'blog-masonary-wrapper ' + this.cls,
30517             cn : {
30518                 cls : 'mas-boxes masonary'
30519             }
30520         };
30521         
30522         return cfg;
30523     },
30524     
30525     getChildContainer: function( )
30526     {
30527         if (this.boxesEl) {
30528             return this.boxesEl;
30529         }
30530         
30531         this.boxesEl = this.el.select('.mas-boxes').first();
30532         
30533         return this.boxesEl;
30534     },
30535     
30536     
30537     initEvents : function()
30538     {
30539         var _this = this;
30540         
30541         if(this.isAutoInitial){
30542             Roo.log('hook children rendered');
30543             this.on('childrenrendered', function() {
30544                 Roo.log('children rendered');
30545                 _this.initial();
30546             } ,this);
30547         }
30548     },
30549     
30550     initial : function()
30551     {
30552         this.selectedBrick = [];
30553         
30554         this.currentSize = this.el.getBox(true);
30555         
30556         Roo.EventManager.onWindowResize(this.resize, this); 
30557
30558         if(!this.isAutoInitial){
30559             this.layout();
30560             return;
30561         }
30562         
30563         this.layout();
30564         
30565         return;
30566         //this.layout.defer(500,this);
30567         
30568     },
30569     
30570     resize : function()
30571     {
30572         var cs = this.el.getBox(true);
30573         
30574         if (
30575                 this.currentSize.width == cs.width && 
30576                 this.currentSize.x == cs.x && 
30577                 this.currentSize.height == cs.height && 
30578                 this.currentSize.y == cs.y 
30579         ) {
30580             Roo.log("no change in with or X or Y");
30581             return;
30582         }
30583         
30584         this.currentSize = cs;
30585         
30586         this.layout();
30587         
30588     },
30589     
30590     layout : function()
30591     {   
30592         this._resetLayout();
30593         
30594         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30595         
30596         this.layoutItems( isInstant );
30597       
30598         this._isLayoutInited = true;
30599         
30600         this.fireEvent('layout', this);
30601         
30602     },
30603     
30604     _resetLayout : function()
30605     {
30606         if(this.isHorizontal){
30607             this.horizontalMeasureColumns();
30608             return;
30609         }
30610         
30611         this.verticalMeasureColumns();
30612         
30613     },
30614     
30615     verticalMeasureColumns : function()
30616     {
30617         this.getContainerWidth();
30618         
30619 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30620 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30621 //            return;
30622 //        }
30623         
30624         var boxWidth = this.boxWidth + this.padWidth;
30625         
30626         if(this.containerWidth < this.boxWidth){
30627             boxWidth = this.containerWidth
30628         }
30629         
30630         var containerWidth = this.containerWidth;
30631         
30632         var cols = Math.floor(containerWidth / boxWidth);
30633         
30634         this.cols = Math.max( cols, 1 );
30635         
30636         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30637         
30638         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30639         
30640         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30641         
30642         this.colWidth = boxWidth + avail - this.padWidth;
30643         
30644         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30645         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30646     },
30647     
30648     horizontalMeasureColumns : function()
30649     {
30650         this.getContainerWidth();
30651         
30652         var boxWidth = this.boxWidth;
30653         
30654         if(this.containerWidth < boxWidth){
30655             boxWidth = this.containerWidth;
30656         }
30657         
30658         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30659         
30660         this.el.setHeight(boxWidth);
30661         
30662     },
30663     
30664     getContainerWidth : function()
30665     {
30666         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30667     },
30668     
30669     layoutItems : function( isInstant )
30670     {
30671         Roo.log(this.bricks);
30672         
30673         var items = Roo.apply([], this.bricks);
30674         
30675         if(this.isHorizontal){
30676             this._horizontalLayoutItems( items , isInstant );
30677             return;
30678         }
30679         
30680 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30681 //            this._verticalAlternativeLayoutItems( items , isInstant );
30682 //            return;
30683 //        }
30684         
30685         this._verticalLayoutItems( items , isInstant );
30686         
30687     },
30688     
30689     _verticalLayoutItems : function ( items , isInstant)
30690     {
30691         if ( !items || !items.length ) {
30692             return;
30693         }
30694         
30695         var standard = [
30696             ['xs', 'xs', 'xs', 'tall'],
30697             ['xs', 'xs', 'tall'],
30698             ['xs', 'xs', 'sm'],
30699             ['xs', 'xs', 'xs'],
30700             ['xs', 'tall'],
30701             ['xs', 'sm'],
30702             ['xs', 'xs'],
30703             ['xs'],
30704             
30705             ['sm', 'xs', 'xs'],
30706             ['sm', 'xs'],
30707             ['sm'],
30708             
30709             ['tall', 'xs', 'xs', 'xs'],
30710             ['tall', 'xs', 'xs'],
30711             ['tall', 'xs'],
30712             ['tall']
30713             
30714         ];
30715         
30716         var queue = [];
30717         
30718         var boxes = [];
30719         
30720         var box = [];
30721         
30722         Roo.each(items, function(item, k){
30723             
30724             switch (item.size) {
30725                 // these layouts take up a full box,
30726                 case 'md' :
30727                 case 'md-left' :
30728                 case 'md-right' :
30729                 case 'wide' :
30730                     
30731                     if(box.length){
30732                         boxes.push(box);
30733                         box = [];
30734                     }
30735                     
30736                     boxes.push([item]);
30737                     
30738                     break;
30739                     
30740                 case 'xs' :
30741                 case 'sm' :
30742                 case 'tall' :
30743                     
30744                     box.push(item);
30745                     
30746                     break;
30747                 default :
30748                     break;
30749                     
30750             }
30751             
30752         }, this);
30753         
30754         if(box.length){
30755             boxes.push(box);
30756             box = [];
30757         }
30758         
30759         var filterPattern = function(box, length)
30760         {
30761             if(!box.length){
30762                 return;
30763             }
30764             
30765             var match = false;
30766             
30767             var pattern = box.slice(0, length);
30768             
30769             var format = [];
30770             
30771             Roo.each(pattern, function(i){
30772                 format.push(i.size);
30773             }, this);
30774             
30775             Roo.each(standard, function(s){
30776                 
30777                 if(String(s) != String(format)){
30778                     return;
30779                 }
30780                 
30781                 match = true;
30782                 return false;
30783                 
30784             }, this);
30785             
30786             if(!match && length == 1){
30787                 return;
30788             }
30789             
30790             if(!match){
30791                 filterPattern(box, length - 1);
30792                 return;
30793             }
30794                 
30795             queue.push(pattern);
30796
30797             box = box.slice(length, box.length);
30798
30799             filterPattern(box, 4);
30800
30801             return;
30802             
30803         }
30804         
30805         Roo.each(boxes, function(box, k){
30806             
30807             if(!box.length){
30808                 return;
30809             }
30810             
30811             if(box.length == 1){
30812                 queue.push(box);
30813                 return;
30814             }
30815             
30816             filterPattern(box, 4);
30817             
30818         }, this);
30819         
30820         this._processVerticalLayoutQueue( queue, isInstant );
30821         
30822     },
30823     
30824 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30825 //    {
30826 //        if ( !items || !items.length ) {
30827 //            return;
30828 //        }
30829 //
30830 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30831 //        
30832 //    },
30833     
30834     _horizontalLayoutItems : function ( items , isInstant)
30835     {
30836         if ( !items || !items.length || items.length < 3) {
30837             return;
30838         }
30839         
30840         items.reverse();
30841         
30842         var eItems = items.slice(0, 3);
30843         
30844         items = items.slice(3, items.length);
30845         
30846         var standard = [
30847             ['xs', 'xs', 'xs', 'wide'],
30848             ['xs', 'xs', 'wide'],
30849             ['xs', 'xs', 'sm'],
30850             ['xs', 'xs', 'xs'],
30851             ['xs', 'wide'],
30852             ['xs', 'sm'],
30853             ['xs', 'xs'],
30854             ['xs'],
30855             
30856             ['sm', 'xs', 'xs'],
30857             ['sm', 'xs'],
30858             ['sm'],
30859             
30860             ['wide', 'xs', 'xs', 'xs'],
30861             ['wide', 'xs', 'xs'],
30862             ['wide', 'xs'],
30863             ['wide'],
30864             
30865             ['wide-thin']
30866         ];
30867         
30868         var queue = [];
30869         
30870         var boxes = [];
30871         
30872         var box = [];
30873         
30874         Roo.each(items, function(item, k){
30875             
30876             switch (item.size) {
30877                 case 'md' :
30878                 case 'md-left' :
30879                 case 'md-right' :
30880                 case 'tall' :
30881                     
30882                     if(box.length){
30883                         boxes.push(box);
30884                         box = [];
30885                     }
30886                     
30887                     boxes.push([item]);
30888                     
30889                     break;
30890                     
30891                 case 'xs' :
30892                 case 'sm' :
30893                 case 'wide' :
30894                 case 'wide-thin' :
30895                     
30896                     box.push(item);
30897                     
30898                     break;
30899                 default :
30900                     break;
30901                     
30902             }
30903             
30904         }, this);
30905         
30906         if(box.length){
30907             boxes.push(box);
30908             box = [];
30909         }
30910         
30911         var filterPattern = function(box, length)
30912         {
30913             if(!box.length){
30914                 return;
30915             }
30916             
30917             var match = false;
30918             
30919             var pattern = box.slice(0, length);
30920             
30921             var format = [];
30922             
30923             Roo.each(pattern, function(i){
30924                 format.push(i.size);
30925             }, this);
30926             
30927             Roo.each(standard, function(s){
30928                 
30929                 if(String(s) != String(format)){
30930                     return;
30931                 }
30932                 
30933                 match = true;
30934                 return false;
30935                 
30936             }, this);
30937             
30938             if(!match && length == 1){
30939                 return;
30940             }
30941             
30942             if(!match){
30943                 filterPattern(box, length - 1);
30944                 return;
30945             }
30946                 
30947             queue.push(pattern);
30948
30949             box = box.slice(length, box.length);
30950
30951             filterPattern(box, 4);
30952
30953             return;
30954             
30955         }
30956         
30957         Roo.each(boxes, function(box, k){
30958             
30959             if(!box.length){
30960                 return;
30961             }
30962             
30963             if(box.length == 1){
30964                 queue.push(box);
30965                 return;
30966             }
30967             
30968             filterPattern(box, 4);
30969             
30970         }, this);
30971         
30972         
30973         var prune = [];
30974         
30975         var pos = this.el.getBox(true);
30976         
30977         var minX = pos.x;
30978         
30979         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30980         
30981         var hit_end = false;
30982         
30983         Roo.each(queue, function(box){
30984             
30985             if(hit_end){
30986                 
30987                 Roo.each(box, function(b){
30988                 
30989                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30990                     b.el.hide();
30991
30992                 }, this);
30993
30994                 return;
30995             }
30996             
30997             var mx = 0;
30998             
30999             Roo.each(box, function(b){
31000                 
31001                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31002                 b.el.show();
31003
31004                 mx = Math.max(mx, b.x);
31005                 
31006             }, this);
31007             
31008             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31009             
31010             if(maxX < minX){
31011                 
31012                 Roo.each(box, function(b){
31013                 
31014                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31015                     b.el.hide();
31016                     
31017                 }, this);
31018                 
31019                 hit_end = true;
31020                 
31021                 return;
31022             }
31023             
31024             prune.push(box);
31025             
31026         }, this);
31027         
31028         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31029     },
31030     
31031     /** Sets position of item in DOM
31032     * @param {Element} item
31033     * @param {Number} x - horizontal position
31034     * @param {Number} y - vertical position
31035     * @param {Boolean} isInstant - disables transitions
31036     */
31037     _processVerticalLayoutQueue : function( queue, isInstant )
31038     {
31039         var pos = this.el.getBox(true);
31040         var x = pos.x;
31041         var y = pos.y;
31042         var maxY = [];
31043         
31044         for (var i = 0; i < this.cols; i++){
31045             maxY[i] = pos.y;
31046         }
31047         
31048         Roo.each(queue, function(box, k){
31049             
31050             var col = k % this.cols;
31051             
31052             Roo.each(box, function(b,kk){
31053                 
31054                 b.el.position('absolute');
31055                 
31056                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31057                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31058                 
31059                 if(b.size == 'md-left' || b.size == 'md-right'){
31060                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31061                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31062                 }
31063                 
31064                 b.el.setWidth(width);
31065                 b.el.setHeight(height);
31066                 // iframe?
31067                 b.el.select('iframe',true).setSize(width,height);
31068                 
31069             }, this);
31070             
31071             for (var i = 0; i < this.cols; i++){
31072                 
31073                 if(maxY[i] < maxY[col]){
31074                     col = i;
31075                     continue;
31076                 }
31077                 
31078                 col = Math.min(col, i);
31079                 
31080             }
31081             
31082             x = pos.x + col * (this.colWidth + this.padWidth);
31083             
31084             y = maxY[col];
31085             
31086             var positions = [];
31087             
31088             switch (box.length){
31089                 case 1 :
31090                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31091                     break;
31092                 case 2 :
31093                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31094                     break;
31095                 case 3 :
31096                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31097                     break;
31098                 case 4 :
31099                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31100                     break;
31101                 default :
31102                     break;
31103             }
31104             
31105             Roo.each(box, function(b,kk){
31106                 
31107                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31108                 
31109                 var sz = b.el.getSize();
31110                 
31111                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31112                 
31113             }, this);
31114             
31115         }, this);
31116         
31117         var mY = 0;
31118         
31119         for (var i = 0; i < this.cols; i++){
31120             mY = Math.max(mY, maxY[i]);
31121         }
31122         
31123         this.el.setHeight(mY - pos.y);
31124         
31125     },
31126     
31127 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31128 //    {
31129 //        var pos = this.el.getBox(true);
31130 //        var x = pos.x;
31131 //        var y = pos.y;
31132 //        var maxX = pos.right;
31133 //        
31134 //        var maxHeight = 0;
31135 //        
31136 //        Roo.each(items, function(item, k){
31137 //            
31138 //            var c = k % 2;
31139 //            
31140 //            item.el.position('absolute');
31141 //                
31142 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31143 //
31144 //            item.el.setWidth(width);
31145 //
31146 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31147 //
31148 //            item.el.setHeight(height);
31149 //            
31150 //            if(c == 0){
31151 //                item.el.setXY([x, y], isInstant ? false : true);
31152 //            } else {
31153 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31154 //            }
31155 //            
31156 //            y = y + height + this.alternativePadWidth;
31157 //            
31158 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31159 //            
31160 //        }, this);
31161 //        
31162 //        this.el.setHeight(maxHeight);
31163 //        
31164 //    },
31165     
31166     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31167     {
31168         var pos = this.el.getBox(true);
31169         
31170         var minX = pos.x;
31171         var minY = pos.y;
31172         
31173         var maxX = pos.right;
31174         
31175         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31176         
31177         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31178         
31179         Roo.each(queue, function(box, k){
31180             
31181             Roo.each(box, function(b, kk){
31182                 
31183                 b.el.position('absolute');
31184                 
31185                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31186                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31187                 
31188                 if(b.size == 'md-left' || b.size == 'md-right'){
31189                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31190                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31191                 }
31192                 
31193                 b.el.setWidth(width);
31194                 b.el.setHeight(height);
31195                 
31196             }, this);
31197             
31198             if(!box.length){
31199                 return;
31200             }
31201             
31202             var positions = [];
31203             
31204             switch (box.length){
31205                 case 1 :
31206                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31207                     break;
31208                 case 2 :
31209                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31210                     break;
31211                 case 3 :
31212                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31213                     break;
31214                 case 4 :
31215                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31216                     break;
31217                 default :
31218                     break;
31219             }
31220             
31221             Roo.each(box, function(b,kk){
31222                 
31223                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31224                 
31225                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31226                 
31227             }, this);
31228             
31229         }, this);
31230         
31231     },
31232     
31233     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31234     {
31235         Roo.each(eItems, function(b,k){
31236             
31237             b.size = (k == 0) ? 'sm' : 'xs';
31238             b.x = (k == 0) ? 2 : 1;
31239             b.y = (k == 0) ? 2 : 1;
31240             
31241             b.el.position('absolute');
31242             
31243             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31244                 
31245             b.el.setWidth(width);
31246             
31247             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31248             
31249             b.el.setHeight(height);
31250             
31251         }, this);
31252
31253         var positions = [];
31254         
31255         positions.push({
31256             x : maxX - this.unitWidth * 2 - this.gutter,
31257             y : minY
31258         });
31259         
31260         positions.push({
31261             x : maxX - this.unitWidth,
31262             y : minY + (this.unitWidth + this.gutter) * 2
31263         });
31264         
31265         positions.push({
31266             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31267             y : minY
31268         });
31269         
31270         Roo.each(eItems, function(b,k){
31271             
31272             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31273
31274         }, this);
31275         
31276     },
31277     
31278     getVerticalOneBoxColPositions : function(x, y, box)
31279     {
31280         var pos = [];
31281         
31282         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31283         
31284         if(box[0].size == 'md-left'){
31285             rand = 0;
31286         }
31287         
31288         if(box[0].size == 'md-right'){
31289             rand = 1;
31290         }
31291         
31292         pos.push({
31293             x : x + (this.unitWidth + this.gutter) * rand,
31294             y : y
31295         });
31296         
31297         return pos;
31298     },
31299     
31300     getVerticalTwoBoxColPositions : function(x, y, box)
31301     {
31302         var pos = [];
31303         
31304         if(box[0].size == 'xs'){
31305             
31306             pos.push({
31307                 x : x,
31308                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31309             });
31310
31311             pos.push({
31312                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31313                 y : y
31314             });
31315             
31316             return pos;
31317             
31318         }
31319         
31320         pos.push({
31321             x : x,
31322             y : y
31323         });
31324
31325         pos.push({
31326             x : x + (this.unitWidth + this.gutter) * 2,
31327             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31328         });
31329         
31330         return pos;
31331         
31332     },
31333     
31334     getVerticalThreeBoxColPositions : function(x, y, box)
31335     {
31336         var pos = [];
31337         
31338         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31339             
31340             pos.push({
31341                 x : x,
31342                 y : y
31343             });
31344
31345             pos.push({
31346                 x : x + (this.unitWidth + this.gutter) * 1,
31347                 y : y
31348             });
31349             
31350             pos.push({
31351                 x : x + (this.unitWidth + this.gutter) * 2,
31352                 y : y
31353             });
31354             
31355             return pos;
31356             
31357         }
31358         
31359         if(box[0].size == 'xs' && box[1].size == 'xs'){
31360             
31361             pos.push({
31362                 x : x,
31363                 y : y
31364             });
31365
31366             pos.push({
31367                 x : x,
31368                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31369             });
31370             
31371             pos.push({
31372                 x : x + (this.unitWidth + this.gutter) * 1,
31373                 y : y
31374             });
31375             
31376             return pos;
31377             
31378         }
31379         
31380         pos.push({
31381             x : x,
31382             y : y
31383         });
31384
31385         pos.push({
31386             x : x + (this.unitWidth + this.gutter) * 2,
31387             y : y
31388         });
31389
31390         pos.push({
31391             x : x + (this.unitWidth + this.gutter) * 2,
31392             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31393         });
31394             
31395         return pos;
31396         
31397     },
31398     
31399     getVerticalFourBoxColPositions : function(x, y, box)
31400     {
31401         var pos = [];
31402         
31403         if(box[0].size == 'xs'){
31404             
31405             pos.push({
31406                 x : x,
31407                 y : y
31408             });
31409
31410             pos.push({
31411                 x : x,
31412                 y : y + (this.unitHeight + this.gutter) * 1
31413             });
31414             
31415             pos.push({
31416                 x : x,
31417                 y : y + (this.unitHeight + this.gutter) * 2
31418             });
31419             
31420             pos.push({
31421                 x : x + (this.unitWidth + this.gutter) * 1,
31422                 y : y
31423             });
31424             
31425             return pos;
31426             
31427         }
31428         
31429         pos.push({
31430             x : x,
31431             y : y
31432         });
31433
31434         pos.push({
31435             x : x + (this.unitWidth + this.gutter) * 2,
31436             y : y
31437         });
31438
31439         pos.push({
31440             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31441             y : y + (this.unitHeight + this.gutter) * 1
31442         });
31443
31444         pos.push({
31445             x : x + (this.unitWidth + this.gutter) * 2,
31446             y : y + (this.unitWidth + this.gutter) * 2
31447         });
31448
31449         return pos;
31450         
31451     },
31452     
31453     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31454     {
31455         var pos = [];
31456         
31457         if(box[0].size == 'md-left'){
31458             pos.push({
31459                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31460                 y : minY
31461             });
31462             
31463             return pos;
31464         }
31465         
31466         if(box[0].size == 'md-right'){
31467             pos.push({
31468                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31469                 y : minY + (this.unitWidth + this.gutter) * 1
31470             });
31471             
31472             return pos;
31473         }
31474         
31475         var rand = Math.floor(Math.random() * (4 - box[0].y));
31476         
31477         pos.push({
31478             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31479             y : minY + (this.unitWidth + this.gutter) * rand
31480         });
31481         
31482         return pos;
31483         
31484     },
31485     
31486     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31487     {
31488         var pos = [];
31489         
31490         if(box[0].size == 'xs'){
31491             
31492             pos.push({
31493                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31494                 y : minY
31495             });
31496
31497             pos.push({
31498                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31499                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31500             });
31501             
31502             return pos;
31503             
31504         }
31505         
31506         pos.push({
31507             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31508             y : minY
31509         });
31510
31511         pos.push({
31512             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31513             y : minY + (this.unitWidth + this.gutter) * 2
31514         });
31515         
31516         return pos;
31517         
31518     },
31519     
31520     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31521     {
31522         var pos = [];
31523         
31524         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31525             
31526             pos.push({
31527                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31528                 y : minY
31529             });
31530
31531             pos.push({
31532                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31533                 y : minY + (this.unitWidth + this.gutter) * 1
31534             });
31535             
31536             pos.push({
31537                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31538                 y : minY + (this.unitWidth + this.gutter) * 2
31539             });
31540             
31541             return pos;
31542             
31543         }
31544         
31545         if(box[0].size == 'xs' && box[1].size == 'xs'){
31546             
31547             pos.push({
31548                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31549                 y : minY
31550             });
31551
31552             pos.push({
31553                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31554                 y : minY
31555             });
31556             
31557             pos.push({
31558                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31559                 y : minY + (this.unitWidth + this.gutter) * 1
31560             });
31561             
31562             return pos;
31563             
31564         }
31565         
31566         pos.push({
31567             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31568             y : minY
31569         });
31570
31571         pos.push({
31572             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31573             y : minY + (this.unitWidth + this.gutter) * 2
31574         });
31575
31576         pos.push({
31577             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31578             y : minY + (this.unitWidth + this.gutter) * 2
31579         });
31580             
31581         return pos;
31582         
31583     },
31584     
31585     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31586     {
31587         var pos = [];
31588         
31589         if(box[0].size == 'xs'){
31590             
31591             pos.push({
31592                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31593                 y : minY
31594             });
31595
31596             pos.push({
31597                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31598                 y : minY
31599             });
31600             
31601             pos.push({
31602                 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),
31603                 y : minY
31604             });
31605             
31606             pos.push({
31607                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31608                 y : minY + (this.unitWidth + this.gutter) * 1
31609             });
31610             
31611             return pos;
31612             
31613         }
31614         
31615         pos.push({
31616             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31617             y : minY
31618         });
31619         
31620         pos.push({
31621             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31622             y : minY + (this.unitWidth + this.gutter) * 2
31623         });
31624         
31625         pos.push({
31626             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31627             y : minY + (this.unitWidth + this.gutter) * 2
31628         });
31629         
31630         pos.push({
31631             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),
31632             y : minY + (this.unitWidth + this.gutter) * 2
31633         });
31634
31635         return pos;
31636         
31637     },
31638     
31639     /**
31640     * remove a Masonry Brick
31641     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31642     */
31643     removeBrick : function(brick_id)
31644     {
31645         if (!brick_id) {
31646             return;
31647         }
31648         
31649         for (var i = 0; i<this.bricks.length; i++) {
31650             if (this.bricks[i].id == brick_id) {
31651                 this.bricks.splice(i,1);
31652                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31653                 this.initial();
31654             }
31655         }
31656     },
31657     
31658     /**
31659     * adds a Masonry Brick
31660     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31661     */
31662     addBrick : function(cfg)
31663     {
31664         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31665         //this.register(cn);
31666         cn.parentId = this.id;
31667         cn.onRender(this.el, null);
31668         return cn;
31669     },
31670     
31671     /**
31672     * register a Masonry Brick
31673     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31674     */
31675     
31676     register : function(brick)
31677     {
31678         this.bricks.push(brick);
31679         brick.masonryId = this.id;
31680     },
31681     
31682     /**
31683     * clear all the Masonry Brick
31684     */
31685     clearAll : function()
31686     {
31687         this.bricks = [];
31688         //this.getChildContainer().dom.innerHTML = "";
31689         this.el.dom.innerHTML = '';
31690     },
31691     
31692     getSelected : function()
31693     {
31694         if (!this.selectedBrick) {
31695             return false;
31696         }
31697         
31698         return this.selectedBrick;
31699     }
31700 });
31701
31702 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31703     
31704     groups: {},
31705      /**
31706     * register a Masonry Layout
31707     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31708     */
31709     
31710     register : function(layout)
31711     {
31712         this.groups[layout.id] = layout;
31713     },
31714     /**
31715     * fetch a  Masonry Layout based on the masonry layout ID
31716     * @param {string} the masonry layout to add
31717     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31718     */
31719     
31720     get: function(layout_id) {
31721         if (typeof(this.groups[layout_id]) == 'undefined') {
31722             return false;
31723         }
31724         return this.groups[layout_id] ;
31725     }
31726     
31727     
31728     
31729 });
31730
31731  
31732
31733  /**
31734  *
31735  * This is based on 
31736  * http://masonry.desandro.com
31737  *
31738  * The idea is to render all the bricks based on vertical width...
31739  *
31740  * The original code extends 'outlayer' - we might need to use that....
31741  * 
31742  */
31743
31744
31745 /**
31746  * @class Roo.bootstrap.LayoutMasonryAuto
31747  * @extends Roo.bootstrap.Component
31748  * Bootstrap Layout Masonry class
31749  * 
31750  * @constructor
31751  * Create a new Element
31752  * @param {Object} config The config object
31753  */
31754
31755 Roo.bootstrap.LayoutMasonryAuto = function(config){
31756     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31757 };
31758
31759 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31760     
31761       /**
31762      * @cfg {Boolean} isFitWidth  - resize the width..
31763      */   
31764     isFitWidth : false,  // options..
31765     /**
31766      * @cfg {Boolean} isOriginLeft = left align?
31767      */   
31768     isOriginLeft : true,
31769     /**
31770      * @cfg {Boolean} isOriginTop = top align?
31771      */   
31772     isOriginTop : false,
31773     /**
31774      * @cfg {Boolean} isLayoutInstant = no animation?
31775      */   
31776     isLayoutInstant : false, // needed?
31777     /**
31778      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31779      */   
31780     isResizingContainer : true,
31781     /**
31782      * @cfg {Number} columnWidth  width of the columns 
31783      */   
31784     
31785     columnWidth : 0,
31786     
31787     /**
31788      * @cfg {Number} maxCols maximum number of columns
31789      */   
31790     
31791     maxCols: 0,
31792     /**
31793      * @cfg {Number} padHeight padding below box..
31794      */   
31795     
31796     padHeight : 10, 
31797     
31798     /**
31799      * @cfg {Boolean} isAutoInitial defalut true
31800      */   
31801     
31802     isAutoInitial : true, 
31803     
31804     // private?
31805     gutter : 0,
31806     
31807     containerWidth: 0,
31808     initialColumnWidth : 0,
31809     currentSize : null,
31810     
31811     colYs : null, // array.
31812     maxY : 0,
31813     padWidth: 10,
31814     
31815     
31816     tag: 'div',
31817     cls: '',
31818     bricks: null, //CompositeElement
31819     cols : 0, // array?
31820     // element : null, // wrapped now this.el
31821     _isLayoutInited : null, 
31822     
31823     
31824     getAutoCreate : function(){
31825         
31826         var cfg = {
31827             tag: this.tag,
31828             cls: 'blog-masonary-wrapper ' + this.cls,
31829             cn : {
31830                 cls : 'mas-boxes masonary'
31831             }
31832         };
31833         
31834         return cfg;
31835     },
31836     
31837     getChildContainer: function( )
31838     {
31839         if (this.boxesEl) {
31840             return this.boxesEl;
31841         }
31842         
31843         this.boxesEl = this.el.select('.mas-boxes').first();
31844         
31845         return this.boxesEl;
31846     },
31847     
31848     
31849     initEvents : function()
31850     {
31851         var _this = this;
31852         
31853         if(this.isAutoInitial){
31854             Roo.log('hook children rendered');
31855             this.on('childrenrendered', function() {
31856                 Roo.log('children rendered');
31857                 _this.initial();
31858             } ,this);
31859         }
31860         
31861     },
31862     
31863     initial : function()
31864     {
31865         this.reloadItems();
31866
31867         this.currentSize = this.el.getBox(true);
31868
31869         /// was window resize... - let's see if this works..
31870         Roo.EventManager.onWindowResize(this.resize, this); 
31871
31872         if(!this.isAutoInitial){
31873             this.layout();
31874             return;
31875         }
31876         
31877         this.layout.defer(500,this);
31878     },
31879     
31880     reloadItems: function()
31881     {
31882         this.bricks = this.el.select('.masonry-brick', true);
31883         
31884         this.bricks.each(function(b) {
31885             //Roo.log(b.getSize());
31886             if (!b.attr('originalwidth')) {
31887                 b.attr('originalwidth',  b.getSize().width);
31888             }
31889             
31890         });
31891         
31892         Roo.log(this.bricks.elements.length);
31893     },
31894     
31895     resize : function()
31896     {
31897         Roo.log('resize');
31898         var cs = this.el.getBox(true);
31899         
31900         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31901             Roo.log("no change in with or X");
31902             return;
31903         }
31904         this.currentSize = cs;
31905         this.layout();
31906     },
31907     
31908     layout : function()
31909     {
31910          Roo.log('layout');
31911         this._resetLayout();
31912         //this._manageStamps();
31913       
31914         // don't animate first layout
31915         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31916         this.layoutItems( isInstant );
31917       
31918         // flag for initalized
31919         this._isLayoutInited = true;
31920     },
31921     
31922     layoutItems : function( isInstant )
31923     {
31924         //var items = this._getItemsForLayout( this.items );
31925         // original code supports filtering layout items.. we just ignore it..
31926         
31927         this._layoutItems( this.bricks , isInstant );
31928       
31929         this._postLayout();
31930     },
31931     _layoutItems : function ( items , isInstant)
31932     {
31933        //this.fireEvent( 'layout', this, items );
31934     
31935
31936         if ( !items || !items.elements.length ) {
31937           // no items, emit event with empty array
31938             return;
31939         }
31940
31941         var queue = [];
31942         items.each(function(item) {
31943             Roo.log("layout item");
31944             Roo.log(item);
31945             // get x/y object from method
31946             var position = this._getItemLayoutPosition( item );
31947             // enqueue
31948             position.item = item;
31949             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31950             queue.push( position );
31951         }, this);
31952       
31953         this._processLayoutQueue( queue );
31954     },
31955     /** Sets position of item in DOM
31956     * @param {Element} item
31957     * @param {Number} x - horizontal position
31958     * @param {Number} y - vertical position
31959     * @param {Boolean} isInstant - disables transitions
31960     */
31961     _processLayoutQueue : function( queue )
31962     {
31963         for ( var i=0, len = queue.length; i < len; i++ ) {
31964             var obj = queue[i];
31965             obj.item.position('absolute');
31966             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31967         }
31968     },
31969       
31970     
31971     /**
31972     * Any logic you want to do after each layout,
31973     * i.e. size the container
31974     */
31975     _postLayout : function()
31976     {
31977         this.resizeContainer();
31978     },
31979     
31980     resizeContainer : function()
31981     {
31982         if ( !this.isResizingContainer ) {
31983             return;
31984         }
31985         var size = this._getContainerSize();
31986         if ( size ) {
31987             this.el.setSize(size.width,size.height);
31988             this.boxesEl.setSize(size.width,size.height);
31989         }
31990     },
31991     
31992     
31993     
31994     _resetLayout : function()
31995     {
31996         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31997         this.colWidth = this.el.getWidth();
31998         //this.gutter = this.el.getWidth(); 
31999         
32000         this.measureColumns();
32001
32002         // reset column Y
32003         var i = this.cols;
32004         this.colYs = [];
32005         while (i--) {
32006             this.colYs.push( 0 );
32007         }
32008     
32009         this.maxY = 0;
32010     },
32011
32012     measureColumns : function()
32013     {
32014         this.getContainerWidth();
32015       // if columnWidth is 0, default to outerWidth of first item
32016         if ( !this.columnWidth ) {
32017             var firstItem = this.bricks.first();
32018             Roo.log(firstItem);
32019             this.columnWidth  = this.containerWidth;
32020             if (firstItem && firstItem.attr('originalwidth') ) {
32021                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32022             }
32023             // columnWidth fall back to item of first element
32024             Roo.log("set column width?");
32025                         this.initialColumnWidth = this.columnWidth  ;
32026
32027             // if first elem has no width, default to size of container
32028             
32029         }
32030         
32031         
32032         if (this.initialColumnWidth) {
32033             this.columnWidth = this.initialColumnWidth;
32034         }
32035         
32036         
32037             
32038         // column width is fixed at the top - however if container width get's smaller we should
32039         // reduce it...
32040         
32041         // this bit calcs how man columns..
32042             
32043         var columnWidth = this.columnWidth += this.gutter;
32044       
32045         // calculate columns
32046         var containerWidth = this.containerWidth + this.gutter;
32047         
32048         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32049         // fix rounding errors, typically with gutters
32050         var excess = columnWidth - containerWidth % columnWidth;
32051         
32052         
32053         // if overshoot is less than a pixel, round up, otherwise floor it
32054         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32055         cols = Math[ mathMethod ]( cols );
32056         this.cols = Math.max( cols, 1 );
32057         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32058         
32059          // padding positioning..
32060         var totalColWidth = this.cols * this.columnWidth;
32061         var padavail = this.containerWidth - totalColWidth;
32062         // so for 2 columns - we need 3 'pads'
32063         
32064         var padNeeded = (1+this.cols) * this.padWidth;
32065         
32066         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32067         
32068         this.columnWidth += padExtra
32069         //this.padWidth = Math.floor(padavail /  ( this.cols));
32070         
32071         // adjust colum width so that padding is fixed??
32072         
32073         // we have 3 columns ... total = width * 3
32074         // we have X left over... that should be used by 
32075         
32076         //if (this.expandC) {
32077             
32078         //}
32079         
32080         
32081         
32082     },
32083     
32084     getContainerWidth : function()
32085     {
32086        /* // container is parent if fit width
32087         var container = this.isFitWidth ? this.element.parentNode : this.element;
32088         // check that this.size and size are there
32089         // IE8 triggers resize on body size change, so they might not be
32090         
32091         var size = getSize( container );  //FIXME
32092         this.containerWidth = size && size.innerWidth; //FIXME
32093         */
32094          
32095         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32096         
32097     },
32098     
32099     _getItemLayoutPosition : function( item )  // what is item?
32100     {
32101         // we resize the item to our columnWidth..
32102       
32103         item.setWidth(this.columnWidth);
32104         item.autoBoxAdjust  = false;
32105         
32106         var sz = item.getSize();
32107  
32108         // how many columns does this brick span
32109         var remainder = this.containerWidth % this.columnWidth;
32110         
32111         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32112         // round if off by 1 pixel, otherwise use ceil
32113         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32114         colSpan = Math.min( colSpan, this.cols );
32115         
32116         // normally this should be '1' as we dont' currently allow multi width columns..
32117         
32118         var colGroup = this._getColGroup( colSpan );
32119         // get the minimum Y value from the columns
32120         var minimumY = Math.min.apply( Math, colGroup );
32121         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32122         
32123         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32124          
32125         // position the brick
32126         var position = {
32127             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32128             y: this.currentSize.y + minimumY + this.padHeight
32129         };
32130         
32131         Roo.log(position);
32132         // apply setHeight to necessary columns
32133         var setHeight = minimumY + sz.height + this.padHeight;
32134         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32135         
32136         var setSpan = this.cols + 1 - colGroup.length;
32137         for ( var i = 0; i < setSpan; i++ ) {
32138           this.colYs[ shortColIndex + i ] = setHeight ;
32139         }
32140       
32141         return position;
32142     },
32143     
32144     /**
32145      * @param {Number} colSpan - number of columns the element spans
32146      * @returns {Array} colGroup
32147      */
32148     _getColGroup : function( colSpan )
32149     {
32150         if ( colSpan < 2 ) {
32151           // if brick spans only one column, use all the column Ys
32152           return this.colYs;
32153         }
32154       
32155         var colGroup = [];
32156         // how many different places could this brick fit horizontally
32157         var groupCount = this.cols + 1 - colSpan;
32158         // for each group potential horizontal position
32159         for ( var i = 0; i < groupCount; i++ ) {
32160           // make an array of colY values for that one group
32161           var groupColYs = this.colYs.slice( i, i + colSpan );
32162           // and get the max value of the array
32163           colGroup[i] = Math.max.apply( Math, groupColYs );
32164         }
32165         return colGroup;
32166     },
32167     /*
32168     _manageStamp : function( stamp )
32169     {
32170         var stampSize =  stamp.getSize();
32171         var offset = stamp.getBox();
32172         // get the columns that this stamp affects
32173         var firstX = this.isOriginLeft ? offset.x : offset.right;
32174         var lastX = firstX + stampSize.width;
32175         var firstCol = Math.floor( firstX / this.columnWidth );
32176         firstCol = Math.max( 0, firstCol );
32177         
32178         var lastCol = Math.floor( lastX / this.columnWidth );
32179         // lastCol should not go over if multiple of columnWidth #425
32180         lastCol -= lastX % this.columnWidth ? 0 : 1;
32181         lastCol = Math.min( this.cols - 1, lastCol );
32182         
32183         // set colYs to bottom of the stamp
32184         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32185             stampSize.height;
32186             
32187         for ( var i = firstCol; i <= lastCol; i++ ) {
32188           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32189         }
32190     },
32191     */
32192     
32193     _getContainerSize : function()
32194     {
32195         this.maxY = Math.max.apply( Math, this.colYs );
32196         var size = {
32197             height: this.maxY
32198         };
32199       
32200         if ( this.isFitWidth ) {
32201             size.width = this._getContainerFitWidth();
32202         }
32203       
32204         return size;
32205     },
32206     
32207     _getContainerFitWidth : function()
32208     {
32209         var unusedCols = 0;
32210         // count unused columns
32211         var i = this.cols;
32212         while ( --i ) {
32213           if ( this.colYs[i] !== 0 ) {
32214             break;
32215           }
32216           unusedCols++;
32217         }
32218         // fit container to columns that have been used
32219         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32220     },
32221     
32222     needsResizeLayout : function()
32223     {
32224         var previousWidth = this.containerWidth;
32225         this.getContainerWidth();
32226         return previousWidth !== this.containerWidth;
32227     }
32228  
32229 });
32230
32231  
32232
32233  /*
32234  * - LGPL
32235  *
32236  * element
32237  * 
32238  */
32239
32240 /**
32241  * @class Roo.bootstrap.MasonryBrick
32242  * @extends Roo.bootstrap.Component
32243  * Bootstrap MasonryBrick class
32244  * 
32245  * @constructor
32246  * Create a new MasonryBrick
32247  * @param {Object} config The config object
32248  */
32249
32250 Roo.bootstrap.MasonryBrick = function(config){
32251     
32252     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32253     
32254     Roo.bootstrap.MasonryBrick.register(this);
32255     
32256     this.addEvents({
32257         // raw events
32258         /**
32259          * @event click
32260          * When a MasonryBrick is clcik
32261          * @param {Roo.bootstrap.MasonryBrick} this
32262          * @param {Roo.EventObject} e
32263          */
32264         "click" : true
32265     });
32266 };
32267
32268 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32269     
32270     /**
32271      * @cfg {String} title
32272      */   
32273     title : '',
32274     /**
32275      * @cfg {String} html
32276      */   
32277     html : '',
32278     /**
32279      * @cfg {String} bgimage
32280      */   
32281     bgimage : '',
32282     /**
32283      * @cfg {String} videourl
32284      */   
32285     videourl : '',
32286     /**
32287      * @cfg {String} cls
32288      */   
32289     cls : '',
32290     /**
32291      * @cfg {String} href
32292      */   
32293     href : '',
32294     /**
32295      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32296      */   
32297     size : 'xs',
32298     
32299     /**
32300      * @cfg {String} placetitle (center|bottom)
32301      */   
32302     placetitle : '',
32303     
32304     /**
32305      * @cfg {Boolean} isFitContainer defalut true
32306      */   
32307     isFitContainer : true, 
32308     
32309     /**
32310      * @cfg {Boolean} preventDefault defalut false
32311      */   
32312     preventDefault : false, 
32313     
32314     /**
32315      * @cfg {Boolean} inverse defalut false
32316      */   
32317     maskInverse : false, 
32318     
32319     getAutoCreate : function()
32320     {
32321         if(!this.isFitContainer){
32322             return this.getSplitAutoCreate();
32323         }
32324         
32325         var cls = 'masonry-brick masonry-brick-full';
32326         
32327         if(this.href.length){
32328             cls += ' masonry-brick-link';
32329         }
32330         
32331         if(this.bgimage.length){
32332             cls += ' masonry-brick-image';
32333         }
32334         
32335         if(this.maskInverse){
32336             cls += ' mask-inverse';
32337         }
32338         
32339         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32340             cls += ' enable-mask';
32341         }
32342         
32343         if(this.size){
32344             cls += ' masonry-' + this.size + '-brick';
32345         }
32346         
32347         if(this.placetitle.length){
32348             
32349             switch (this.placetitle) {
32350                 case 'center' :
32351                     cls += ' masonry-center-title';
32352                     break;
32353                 case 'bottom' :
32354                     cls += ' masonry-bottom-title';
32355                     break;
32356                 default:
32357                     break;
32358             }
32359             
32360         } else {
32361             if(!this.html.length && !this.bgimage.length){
32362                 cls += ' masonry-center-title';
32363             }
32364
32365             if(!this.html.length && this.bgimage.length){
32366                 cls += ' masonry-bottom-title';
32367             }
32368         }
32369         
32370         if(this.cls){
32371             cls += ' ' + this.cls;
32372         }
32373         
32374         var cfg = {
32375             tag: (this.href.length) ? 'a' : 'div',
32376             cls: cls,
32377             cn: [
32378                 {
32379                     tag: 'div',
32380                     cls: 'masonry-brick-mask'
32381                 },
32382                 {
32383                     tag: 'div',
32384                     cls: 'masonry-brick-paragraph',
32385                     cn: []
32386                 }
32387             ]
32388         };
32389         
32390         if(this.href.length){
32391             cfg.href = this.href;
32392         }
32393         
32394         var cn = cfg.cn[1].cn;
32395         
32396         if(this.title.length){
32397             cn.push({
32398                 tag: 'h4',
32399                 cls: 'masonry-brick-title',
32400                 html: this.title
32401             });
32402         }
32403         
32404         if(this.html.length){
32405             cn.push({
32406                 tag: 'p',
32407                 cls: 'masonry-brick-text',
32408                 html: this.html
32409             });
32410         }
32411         
32412         if (!this.title.length && !this.html.length) {
32413             cfg.cn[1].cls += ' hide';
32414         }
32415         
32416         if(this.bgimage.length){
32417             cfg.cn.push({
32418                 tag: 'img',
32419                 cls: 'masonry-brick-image-view',
32420                 src: this.bgimage
32421             });
32422         }
32423         
32424         if(this.videourl.length){
32425             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32426             // youtube support only?
32427             cfg.cn.push({
32428                 tag: 'iframe',
32429                 cls: 'masonry-brick-image-view',
32430                 src: vurl,
32431                 frameborder : 0,
32432                 allowfullscreen : true
32433             });
32434         }
32435         
32436         return cfg;
32437         
32438     },
32439     
32440     getSplitAutoCreate : function()
32441     {
32442         var cls = 'masonry-brick masonry-brick-split';
32443         
32444         if(this.href.length){
32445             cls += ' masonry-brick-link';
32446         }
32447         
32448         if(this.bgimage.length){
32449             cls += ' masonry-brick-image';
32450         }
32451         
32452         if(this.size){
32453             cls += ' masonry-' + this.size + '-brick';
32454         }
32455         
32456         switch (this.placetitle) {
32457             case 'center' :
32458                 cls += ' masonry-center-title';
32459                 break;
32460             case 'bottom' :
32461                 cls += ' masonry-bottom-title';
32462                 break;
32463             default:
32464                 if(!this.bgimage.length){
32465                     cls += ' masonry-center-title';
32466                 }
32467
32468                 if(this.bgimage.length){
32469                     cls += ' masonry-bottom-title';
32470                 }
32471                 break;
32472         }
32473         
32474         if(this.cls){
32475             cls += ' ' + this.cls;
32476         }
32477         
32478         var cfg = {
32479             tag: (this.href.length) ? 'a' : 'div',
32480             cls: cls,
32481             cn: [
32482                 {
32483                     tag: 'div',
32484                     cls: 'masonry-brick-split-head',
32485                     cn: [
32486                         {
32487                             tag: 'div',
32488                             cls: 'masonry-brick-paragraph',
32489                             cn: []
32490                         }
32491                     ]
32492                 },
32493                 {
32494                     tag: 'div',
32495                     cls: 'masonry-brick-split-body',
32496                     cn: []
32497                 }
32498             ]
32499         };
32500         
32501         if(this.href.length){
32502             cfg.href = this.href;
32503         }
32504         
32505         if(this.title.length){
32506             cfg.cn[0].cn[0].cn.push({
32507                 tag: 'h4',
32508                 cls: 'masonry-brick-title',
32509                 html: this.title
32510             });
32511         }
32512         
32513         if(this.html.length){
32514             cfg.cn[1].cn.push({
32515                 tag: 'p',
32516                 cls: 'masonry-brick-text',
32517                 html: this.html
32518             });
32519         }
32520
32521         if(this.bgimage.length){
32522             cfg.cn[0].cn.push({
32523                 tag: 'img',
32524                 cls: 'masonry-brick-image-view',
32525                 src: this.bgimage
32526             });
32527         }
32528         
32529         if(this.videourl.length){
32530             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32531             // youtube support only?
32532             cfg.cn[0].cn.cn.push({
32533                 tag: 'iframe',
32534                 cls: 'masonry-brick-image-view',
32535                 src: vurl,
32536                 frameborder : 0,
32537                 allowfullscreen : true
32538             });
32539         }
32540         
32541         return cfg;
32542     },
32543     
32544     initEvents: function() 
32545     {
32546         switch (this.size) {
32547             case 'xs' :
32548                 this.x = 1;
32549                 this.y = 1;
32550                 break;
32551             case 'sm' :
32552                 this.x = 2;
32553                 this.y = 2;
32554                 break;
32555             case 'md' :
32556             case 'md-left' :
32557             case 'md-right' :
32558                 this.x = 3;
32559                 this.y = 3;
32560                 break;
32561             case 'tall' :
32562                 this.x = 2;
32563                 this.y = 3;
32564                 break;
32565             case 'wide' :
32566                 this.x = 3;
32567                 this.y = 2;
32568                 break;
32569             case 'wide-thin' :
32570                 this.x = 3;
32571                 this.y = 1;
32572                 break;
32573                         
32574             default :
32575                 break;
32576         }
32577         
32578         if(Roo.isTouch){
32579             this.el.on('touchstart', this.onTouchStart, this);
32580             this.el.on('touchmove', this.onTouchMove, this);
32581             this.el.on('touchend', this.onTouchEnd, this);
32582             this.el.on('contextmenu', this.onContextMenu, this);
32583         } else {
32584             this.el.on('mouseenter'  ,this.enter, this);
32585             this.el.on('mouseleave', this.leave, this);
32586             this.el.on('click', this.onClick, this);
32587         }
32588         
32589         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32590             this.parent().bricks.push(this);   
32591         }
32592         
32593     },
32594     
32595     onClick: function(e, el)
32596     {
32597         var time = this.endTimer - this.startTimer;
32598         // Roo.log(e.preventDefault());
32599         if(Roo.isTouch){
32600             if(time > 1000){
32601                 e.preventDefault();
32602                 return;
32603             }
32604         }
32605         
32606         if(!this.preventDefault){
32607             return;
32608         }
32609         
32610         e.preventDefault();
32611         
32612         if (this.activcClass != '') {
32613             this.selectBrick();
32614         }
32615         
32616         this.fireEvent('click', this);
32617     },
32618     
32619     enter: function(e, el)
32620     {
32621         e.preventDefault();
32622         
32623         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32624             return;
32625         }
32626         
32627         if(this.bgimage.length && this.html.length){
32628             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32629         }
32630     },
32631     
32632     leave: function(e, el)
32633     {
32634         e.preventDefault();
32635         
32636         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32637             return;
32638         }
32639         
32640         if(this.bgimage.length && this.html.length){
32641             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32642         }
32643     },
32644     
32645     onTouchStart: function(e, el)
32646     {
32647 //        e.preventDefault();
32648         
32649         this.touchmoved = false;
32650         
32651         if(!this.isFitContainer){
32652             return;
32653         }
32654         
32655         if(!this.bgimage.length || !this.html.length){
32656             return;
32657         }
32658         
32659         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32660         
32661         this.timer = new Date().getTime();
32662         
32663     },
32664     
32665     onTouchMove: function(e, el)
32666     {
32667         this.touchmoved = true;
32668     },
32669     
32670     onContextMenu : function(e,el)
32671     {
32672         e.preventDefault();
32673         e.stopPropagation();
32674         return false;
32675     },
32676     
32677     onTouchEnd: function(e, el)
32678     {
32679 //        e.preventDefault();
32680         
32681         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32682         
32683             this.leave(e,el);
32684             
32685             return;
32686         }
32687         
32688         if(!this.bgimage.length || !this.html.length){
32689             
32690             if(this.href.length){
32691                 window.location.href = this.href;
32692             }
32693             
32694             return;
32695         }
32696         
32697         if(!this.isFitContainer){
32698             return;
32699         }
32700         
32701         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32702         
32703         window.location.href = this.href;
32704     },
32705     
32706     //selection on single brick only
32707     selectBrick : function() {
32708         
32709         if (!this.parentId) {
32710             return;
32711         }
32712         
32713         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32714         var index = m.selectedBrick.indexOf(this.id);
32715         
32716         if ( index > -1) {
32717             m.selectedBrick.splice(index,1);
32718             this.el.removeClass(this.activeClass);
32719             return;
32720         }
32721         
32722         for(var i = 0; i < m.selectedBrick.length; i++) {
32723             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32724             b.el.removeClass(b.activeClass);
32725         }
32726         
32727         m.selectedBrick = [];
32728         
32729         m.selectedBrick.push(this.id);
32730         this.el.addClass(this.activeClass);
32731         return;
32732     }
32733     
32734 });
32735
32736 Roo.apply(Roo.bootstrap.MasonryBrick, {
32737     
32738     //groups: {},
32739     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32740      /**
32741     * register a Masonry Brick
32742     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32743     */
32744     
32745     register : function(brick)
32746     {
32747         //this.groups[brick.id] = brick;
32748         this.groups.add(brick.id, brick);
32749     },
32750     /**
32751     * fetch a  masonry brick based on the masonry brick ID
32752     * @param {string} the masonry brick to add
32753     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32754     */
32755     
32756     get: function(brick_id) 
32757     {
32758         // if (typeof(this.groups[brick_id]) == 'undefined') {
32759         //     return false;
32760         // }
32761         // return this.groups[brick_id] ;
32762         
32763         if(this.groups.key(brick_id)) {
32764             return this.groups.key(brick_id);
32765         }
32766         
32767         return false;
32768     }
32769     
32770     
32771     
32772 });
32773
32774  /*
32775  * - LGPL
32776  *
32777  * element
32778  * 
32779  */
32780
32781 /**
32782  * @class Roo.bootstrap.Brick
32783  * @extends Roo.bootstrap.Component
32784  * Bootstrap Brick class
32785  * 
32786  * @constructor
32787  * Create a new Brick
32788  * @param {Object} config The config object
32789  */
32790
32791 Roo.bootstrap.Brick = function(config){
32792     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32793     
32794     this.addEvents({
32795         // raw events
32796         /**
32797          * @event click
32798          * When a Brick is click
32799          * @param {Roo.bootstrap.Brick} this
32800          * @param {Roo.EventObject} e
32801          */
32802         "click" : true
32803     });
32804 };
32805
32806 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32807     
32808     /**
32809      * @cfg {String} title
32810      */   
32811     title : '',
32812     /**
32813      * @cfg {String} html
32814      */   
32815     html : '',
32816     /**
32817      * @cfg {String} bgimage
32818      */   
32819     bgimage : '',
32820     /**
32821      * @cfg {String} cls
32822      */   
32823     cls : '',
32824     /**
32825      * @cfg {String} href
32826      */   
32827     href : '',
32828     /**
32829      * @cfg {String} video
32830      */   
32831     video : '',
32832     /**
32833      * @cfg {Boolean} square
32834      */   
32835     square : true,
32836     
32837     getAutoCreate : function()
32838     {
32839         var cls = 'roo-brick';
32840         
32841         if(this.href.length){
32842             cls += ' roo-brick-link';
32843         }
32844         
32845         if(this.bgimage.length){
32846             cls += ' roo-brick-image';
32847         }
32848         
32849         if(!this.html.length && !this.bgimage.length){
32850             cls += ' roo-brick-center-title';
32851         }
32852         
32853         if(!this.html.length && this.bgimage.length){
32854             cls += ' roo-brick-bottom-title';
32855         }
32856         
32857         if(this.cls){
32858             cls += ' ' + this.cls;
32859         }
32860         
32861         var cfg = {
32862             tag: (this.href.length) ? 'a' : 'div',
32863             cls: cls,
32864             cn: [
32865                 {
32866                     tag: 'div',
32867                     cls: 'roo-brick-paragraph',
32868                     cn: []
32869                 }
32870             ]
32871         };
32872         
32873         if(this.href.length){
32874             cfg.href = this.href;
32875         }
32876         
32877         var cn = cfg.cn[0].cn;
32878         
32879         if(this.title.length){
32880             cn.push({
32881                 tag: 'h4',
32882                 cls: 'roo-brick-title',
32883                 html: this.title
32884             });
32885         }
32886         
32887         if(this.html.length){
32888             cn.push({
32889                 tag: 'p',
32890                 cls: 'roo-brick-text',
32891                 html: this.html
32892             });
32893         } else {
32894             cn.cls += ' hide';
32895         }
32896         
32897         if(this.bgimage.length){
32898             cfg.cn.push({
32899                 tag: 'img',
32900                 cls: 'roo-brick-image-view',
32901                 src: this.bgimage
32902             });
32903         }
32904         
32905         return cfg;
32906     },
32907     
32908     initEvents: function() 
32909     {
32910         if(this.title.length || this.html.length){
32911             this.el.on('mouseenter'  ,this.enter, this);
32912             this.el.on('mouseleave', this.leave, this);
32913         }
32914         
32915         Roo.EventManager.onWindowResize(this.resize, this); 
32916         
32917         if(this.bgimage.length){
32918             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32919             this.imageEl.on('load', this.onImageLoad, this);
32920             return;
32921         }
32922         
32923         this.resize();
32924     },
32925     
32926     onImageLoad : function()
32927     {
32928         this.resize();
32929     },
32930     
32931     resize : function()
32932     {
32933         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32934         
32935         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32936         
32937         if(this.bgimage.length){
32938             var image = this.el.select('.roo-brick-image-view', true).first();
32939             
32940             image.setWidth(paragraph.getWidth());
32941             
32942             if(this.square){
32943                 image.setHeight(paragraph.getWidth());
32944             }
32945             
32946             this.el.setHeight(image.getHeight());
32947             paragraph.setHeight(image.getHeight());
32948             
32949         }
32950         
32951     },
32952     
32953     enter: function(e, el)
32954     {
32955         e.preventDefault();
32956         
32957         if(this.bgimage.length){
32958             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32959             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32960         }
32961     },
32962     
32963     leave: function(e, el)
32964     {
32965         e.preventDefault();
32966         
32967         if(this.bgimage.length){
32968             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32969             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32970         }
32971     }
32972     
32973 });
32974
32975  
32976
32977  /*
32978  * - LGPL
32979  *
32980  * Input
32981  * 
32982  */
32983
32984 /**
32985  * @class Roo.bootstrap.NumberField
32986  * @extends Roo.bootstrap.Input
32987  * Bootstrap NumberField class
32988  * 
32989  * 
32990  * 
32991  * 
32992  * @constructor
32993  * Create a new NumberField
32994  * @param {Object} config The config object
32995  */
32996
32997 Roo.bootstrap.NumberField = function(config){
32998     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32999 };
33000
33001 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33002     
33003     /**
33004      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33005      */
33006     allowDecimals : true,
33007     /**
33008      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33009      */
33010     decimalSeparator : ".",
33011     /**
33012      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33013      */
33014     decimalPrecision : 2,
33015     /**
33016      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33017      */
33018     allowNegative : true,
33019     /**
33020      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33021      */
33022     minValue : Number.NEGATIVE_INFINITY,
33023     /**
33024      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33025      */
33026     maxValue : Number.MAX_VALUE,
33027     /**
33028      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33029      */
33030     minText : "The minimum value for this field is {0}",
33031     /**
33032      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33033      */
33034     maxText : "The maximum value for this field is {0}",
33035     /**
33036      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33037      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33038      */
33039     nanText : "{0} is not a valid number",
33040     /**
33041      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33042      */
33043     castInt : true,
33044     /**
33045      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33046      */
33047     thousandsDelimiter : false,
33048     /**
33049      * @cfg {String} valueAlign alignment of value
33050      */
33051     valueAlign : "left",
33052
33053     getAutoCreate : function()
33054     {
33055         var hiddenInput = {
33056             tag: 'input',
33057             type: 'hidden',
33058             id: Roo.id(),
33059             cls: 'hidden-number-input'
33060         };
33061         
33062         if (this.name) {
33063             hiddenInput.name = this.name;
33064         }
33065         
33066         this.name = '';
33067         
33068         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33069         
33070         this.name = hiddenInput.name;
33071         
33072         if(cfg.cn.length > 0) {
33073             cfg.cn.push(hiddenInput);
33074         }
33075         
33076         return cfg;
33077     },
33078
33079     // private
33080     initEvents : function()
33081     {   
33082         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33083         
33084         var allowed = "0123456789";
33085         
33086         if(this.allowDecimals){
33087             allowed += this.decimalSeparator;
33088         }
33089         
33090         if(this.allowNegative){
33091             allowed += "-";
33092         }
33093         
33094         if(this.thousandsDelimiter) {
33095             allowed += ",";
33096         }
33097         
33098         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33099         
33100         var keyPress = function(e){
33101             
33102             var k = e.getKey();
33103             
33104             var c = e.getCharCode();
33105             
33106             if(
33107                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33108                     allowed.indexOf(String.fromCharCode(c)) === -1
33109             ){
33110                 e.stopEvent();
33111                 return;
33112             }
33113             
33114             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33115                 return;
33116             }
33117             
33118             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33119                 e.stopEvent();
33120             }
33121         };
33122         
33123         this.el.on("keypress", keyPress, this);
33124     },
33125     
33126     validateValue : function(value)
33127     {
33128         
33129         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33130             return false;
33131         }
33132         
33133         var num = this.parseValue(value);
33134         
33135         if(isNaN(num)){
33136             this.markInvalid(String.format(this.nanText, value));
33137             return false;
33138         }
33139         
33140         if(num < this.minValue){
33141             this.markInvalid(String.format(this.minText, this.minValue));
33142             return false;
33143         }
33144         
33145         if(num > this.maxValue){
33146             this.markInvalid(String.format(this.maxText, this.maxValue));
33147             return false;
33148         }
33149         
33150         return true;
33151     },
33152
33153     getValue : function()
33154     {
33155         var v = this.hiddenEl().getValue();
33156         
33157         return this.fixPrecision(this.parseValue(v));
33158     },
33159
33160     parseValue : function(value)
33161     {
33162         if(this.thousandsDelimiter) {
33163             value += "";
33164             r = new RegExp(",", "g");
33165             value = value.replace(r, "");
33166         }
33167         
33168         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33169         return isNaN(value) ? '' : value;
33170     },
33171
33172     fixPrecision : function(value)
33173     {
33174         if(this.thousandsDelimiter) {
33175             value += "";
33176             r = new RegExp(",", "g");
33177             value = value.replace(r, "");
33178         }
33179         
33180         var nan = isNaN(value);
33181         
33182         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33183             return nan ? '' : value;
33184         }
33185         return parseFloat(value).toFixed(this.decimalPrecision);
33186     },
33187
33188     setValue : function(v)
33189     {
33190         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33191         
33192         this.value = v;
33193         
33194         if(this.rendered){
33195             
33196             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33197             
33198             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
33199                 this.thousandsDelimiter || ''
33200             );
33201             
33202             Roo.log(v);
33203             
33204             if(this.allowBlank && !v) {
33205                 this.inputEl().dom.value = '';
33206             }
33207             
33208             this.validate();
33209         }
33210     },
33211
33212     decimalPrecisionFcn : function(v)
33213     {
33214         return Math.floor(v);
33215     },
33216
33217     beforeBlur : function()
33218     {
33219         if(!this.castInt){
33220             return;
33221         }
33222         
33223         var v = this.parseValue(this.getRawValue());
33224         if(v){
33225             this.setValue(v);
33226         }
33227     },
33228     
33229     hiddenEl : function()
33230     {
33231         return this.el.select('input.hidden-number-input',true).first();
33232     }
33233     
33234 });
33235
33236  
33237
33238 /*
33239 * Licence: LGPL
33240 */
33241
33242 /**
33243  * @class Roo.bootstrap.DocumentSlider
33244  * @extends Roo.bootstrap.Component
33245  * Bootstrap DocumentSlider class
33246  * 
33247  * @constructor
33248  * Create a new DocumentViewer
33249  * @param {Object} config The config object
33250  */
33251
33252 Roo.bootstrap.DocumentSlider = function(config){
33253     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33254     
33255     this.files = [];
33256     
33257     this.addEvents({
33258         /**
33259          * @event initial
33260          * Fire after initEvent
33261          * @param {Roo.bootstrap.DocumentSlider} this
33262          */
33263         "initial" : true,
33264         /**
33265          * @event update
33266          * Fire after update
33267          * @param {Roo.bootstrap.DocumentSlider} this
33268          */
33269         "update" : true,
33270         /**
33271          * @event click
33272          * Fire after click
33273          * @param {Roo.bootstrap.DocumentSlider} this
33274          */
33275         "click" : true
33276     });
33277 };
33278
33279 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33280     
33281     files : false,
33282     
33283     indicator : 0,
33284     
33285     getAutoCreate : function()
33286     {
33287         var cfg = {
33288             tag : 'div',
33289             cls : 'roo-document-slider',
33290             cn : [
33291                 {
33292                     tag : 'div',
33293                     cls : 'roo-document-slider-header',
33294                     cn : [
33295                         {
33296                             tag : 'div',
33297                             cls : 'roo-document-slider-header-title'
33298                         }
33299                     ]
33300                 },
33301                 {
33302                     tag : 'div',
33303                     cls : 'roo-document-slider-body',
33304                     cn : [
33305                         {
33306                             tag : 'div',
33307                             cls : 'roo-document-slider-prev',
33308                             cn : [
33309                                 {
33310                                     tag : 'i',
33311                                     cls : 'fa fa-chevron-left'
33312                                 }
33313                             ]
33314                         },
33315                         {
33316                             tag : 'div',
33317                             cls : 'roo-document-slider-thumb',
33318                             cn : [
33319                                 {
33320                                     tag : 'img',
33321                                     cls : 'roo-document-slider-image'
33322                                 }
33323                             ]
33324                         },
33325                         {
33326                             tag : 'div',
33327                             cls : 'roo-document-slider-next',
33328                             cn : [
33329                                 {
33330                                     tag : 'i',
33331                                     cls : 'fa fa-chevron-right'
33332                                 }
33333                             ]
33334                         }
33335                     ]
33336                 }
33337             ]
33338         };
33339         
33340         return cfg;
33341     },
33342     
33343     initEvents : function()
33344     {
33345         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33346         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33347         
33348         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33349         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33350         
33351         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33352         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33353         
33354         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33355         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33356         
33357         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33358         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33359         
33360         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33361         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33362         
33363         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33364         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33365         
33366         this.thumbEl.on('click', this.onClick, this);
33367         
33368         this.prevIndicator.on('click', this.prev, this);
33369         
33370         this.nextIndicator.on('click', this.next, this);
33371         
33372     },
33373     
33374     initial : function()
33375     {
33376         if(this.files.length){
33377             this.indicator = 1;
33378             this.update()
33379         }
33380         
33381         this.fireEvent('initial', this);
33382     },
33383     
33384     update : function()
33385     {
33386         this.imageEl.attr('src', this.files[this.indicator - 1]);
33387         
33388         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33389         
33390         this.prevIndicator.show();
33391         
33392         if(this.indicator == 1){
33393             this.prevIndicator.hide();
33394         }
33395         
33396         this.nextIndicator.show();
33397         
33398         if(this.indicator == this.files.length){
33399             this.nextIndicator.hide();
33400         }
33401         
33402         this.thumbEl.scrollTo('top');
33403         
33404         this.fireEvent('update', this);
33405     },
33406     
33407     onClick : function(e)
33408     {
33409         e.preventDefault();
33410         
33411         this.fireEvent('click', this);
33412     },
33413     
33414     prev : function(e)
33415     {
33416         e.preventDefault();
33417         
33418         this.indicator = Math.max(1, this.indicator - 1);
33419         
33420         this.update();
33421     },
33422     
33423     next : function(e)
33424     {
33425         e.preventDefault();
33426         
33427         this.indicator = Math.min(this.files.length, this.indicator + 1);
33428         
33429         this.update();
33430     }
33431 });
33432 /*
33433  * - LGPL
33434  *
33435  * RadioSet
33436  *
33437  *
33438  */
33439
33440 /**
33441  * @class Roo.bootstrap.RadioSet
33442  * @extends Roo.bootstrap.Input
33443  * Bootstrap RadioSet class
33444  * @cfg {String} indicatorpos (left|right) default left
33445  * @cfg {Boolean} inline (true|false) inline the element (default true)
33446  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33447  * @constructor
33448  * Create a new RadioSet
33449  * @param {Object} config The config object
33450  */
33451
33452 Roo.bootstrap.RadioSet = function(config){
33453     
33454     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33455     
33456     this.radioes = [];
33457     
33458     Roo.bootstrap.RadioSet.register(this);
33459     
33460     this.addEvents({
33461         /**
33462         * @event check
33463         * Fires when the element is checked or unchecked.
33464         * @param {Roo.bootstrap.RadioSet} this This radio
33465         * @param {Roo.bootstrap.Radio} item The checked item
33466         */
33467        check : true
33468     });
33469     
33470 };
33471
33472 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33473
33474     radioes : false,
33475     
33476     inline : true,
33477     
33478     weight : '',
33479     
33480     indicatorpos : 'left',
33481     
33482     getAutoCreate : function()
33483     {
33484         var label = {
33485             tag : 'label',
33486             cls : 'roo-radio-set-label',
33487             cn : [
33488                 {
33489                     tag : 'span',
33490                     html : this.fieldLabel
33491                 }
33492             ]
33493         };
33494         
33495         if(this.indicatorpos == 'left'){
33496             label.cn.unshift({
33497                 tag : 'i',
33498                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33499                 tooltip : 'This field is required'
33500             });
33501         } else {
33502             label.cn.push({
33503                 tag : 'i',
33504                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33505                 tooltip : 'This field is required'
33506             });
33507         }
33508         
33509         var items = {
33510             tag : 'div',
33511             cls : 'roo-radio-set-items'
33512         };
33513         
33514         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33515         
33516         if (align === 'left' && this.fieldLabel.length) {
33517             
33518             items = {
33519                 cls : "roo-radio-set-right", 
33520                 cn: [
33521                     items
33522                 ]
33523             };
33524             
33525             if(this.labelWidth > 12){
33526                 label.style = "width: " + this.labelWidth + 'px';
33527             }
33528             
33529             if(this.labelWidth < 13 && this.labelmd == 0){
33530                 this.labelmd = this.labelWidth;
33531             }
33532             
33533             if(this.labellg > 0){
33534                 label.cls += ' col-lg-' + this.labellg;
33535                 items.cls += ' col-lg-' + (12 - this.labellg);
33536             }
33537             
33538             if(this.labelmd > 0){
33539                 label.cls += ' col-md-' + this.labelmd;
33540                 items.cls += ' col-md-' + (12 - this.labelmd);
33541             }
33542             
33543             if(this.labelsm > 0){
33544                 label.cls += ' col-sm-' + this.labelsm;
33545                 items.cls += ' col-sm-' + (12 - this.labelsm);
33546             }
33547             
33548             if(this.labelxs > 0){
33549                 label.cls += ' col-xs-' + this.labelxs;
33550                 items.cls += ' col-xs-' + (12 - this.labelxs);
33551             }
33552         }
33553         
33554         var cfg = {
33555             tag : 'div',
33556             cls : 'roo-radio-set',
33557             cn : [
33558                 {
33559                     tag : 'input',
33560                     cls : 'roo-radio-set-input',
33561                     type : 'hidden',
33562                     name : this.name,
33563                     value : this.value ? this.value :  ''
33564                 },
33565                 label,
33566                 items
33567             ]
33568         };
33569         
33570         if(this.weight.length){
33571             cfg.cls += ' roo-radio-' + this.weight;
33572         }
33573         
33574         if(this.inline) {
33575             cfg.cls += ' roo-radio-set-inline';
33576         }
33577         
33578         var settings=this;
33579         ['xs','sm','md','lg'].map(function(size){
33580             if (settings[size]) {
33581                 cfg.cls += ' col-' + size + '-' + settings[size];
33582             }
33583         });
33584         
33585         return cfg;
33586         
33587     },
33588
33589     initEvents : function()
33590     {
33591         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33592         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33593         
33594         if(!this.fieldLabel.length){
33595             this.labelEl.hide();
33596         }
33597         
33598         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33599         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33600         
33601         this.indicatorEl().addClass('invisible');
33602         
33603         this.originalValue = this.getValue();
33604         
33605     },
33606     
33607     inputEl: function ()
33608     {
33609         return this.el.select('.roo-radio-set-input', true).first();
33610     },
33611     
33612     getChildContainer : function()
33613     {
33614         return this.itemsEl;
33615     },
33616     
33617     register : function(item)
33618     {
33619         this.radioes.push(item);
33620         
33621     },
33622     
33623     validate : function()
33624     {   
33625         var valid = false;
33626         
33627         Roo.each(this.radioes, function(i){
33628             if(!i.checked){
33629                 return;
33630             }
33631             
33632             valid = true;
33633             return false;
33634         });
33635         
33636         if(this.allowBlank) {
33637             return true;
33638         }
33639         
33640         if(this.disabled || valid){
33641             this.markValid();
33642             return true;
33643         }
33644         
33645         this.markInvalid();
33646         return false;
33647         
33648     },
33649     
33650     markValid : function()
33651     {
33652         if(this.labelEl.isVisible(true)){
33653             this.indicatorEl().removeClass('visible');
33654             this.indicatorEl().addClass('invisible');
33655         }
33656         
33657         this.el.removeClass([this.invalidClass, this.validClass]);
33658         this.el.addClass(this.validClass);
33659         
33660         this.fireEvent('valid', this);
33661     },
33662     
33663     markInvalid : function(msg)
33664     {
33665         if(this.allowBlank || this.disabled){
33666             return;
33667         }
33668         
33669         if(this.labelEl.isVisible(true)){
33670             this.indicatorEl().removeClass('invisible');
33671             this.indicatorEl().addClass('visible');
33672         }
33673         
33674         this.el.removeClass([this.invalidClass, this.validClass]);
33675         this.el.addClass(this.invalidClass);
33676         
33677         this.fireEvent('invalid', this, msg);
33678         
33679     },
33680     
33681     setValue : function(v, suppressEvent)
33682     {   
33683         if(this.value === v){
33684             return;
33685         }
33686         
33687         this.value = v;
33688         
33689         if(this.rendered){
33690             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33691         }
33692         
33693         Roo.each(this.radioes, function(i){
33694             i.checked = false;
33695             i.el.removeClass('checked');
33696         });
33697         
33698         Roo.each(this.radioes, function(i){
33699             
33700             if(i.value === v || i.value.toString() === v.toString()){
33701                 i.checked = true;
33702                 i.el.addClass('checked');
33703                 
33704                 if(suppressEvent !== true){
33705                     this.fireEvent('check', this, i);
33706                 }
33707                 
33708                 return false;
33709             }
33710             
33711         }, this);
33712         
33713         this.validate();
33714     },
33715     
33716     clearInvalid : function(){
33717         
33718         if(!this.el || this.preventMark){
33719             return;
33720         }
33721         
33722         this.el.removeClass([this.invalidClass]);
33723         
33724         this.fireEvent('valid', this);
33725     }
33726     
33727 });
33728
33729 Roo.apply(Roo.bootstrap.RadioSet, {
33730     
33731     groups: {},
33732     
33733     register : function(set)
33734     {
33735         this.groups[set.name] = set;
33736     },
33737     
33738     get: function(name) 
33739     {
33740         if (typeof(this.groups[name]) == 'undefined') {
33741             return false;
33742         }
33743         
33744         return this.groups[name] ;
33745     }
33746     
33747 });
33748 /*
33749  * Based on:
33750  * Ext JS Library 1.1.1
33751  * Copyright(c) 2006-2007, Ext JS, LLC.
33752  *
33753  * Originally Released Under LGPL - original licence link has changed is not relivant.
33754  *
33755  * Fork - LGPL
33756  * <script type="text/javascript">
33757  */
33758
33759
33760 /**
33761  * @class Roo.bootstrap.SplitBar
33762  * @extends Roo.util.Observable
33763  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33764  * <br><br>
33765  * Usage:
33766  * <pre><code>
33767 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33768                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33769 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33770 split.minSize = 100;
33771 split.maxSize = 600;
33772 split.animate = true;
33773 split.on('moved', splitterMoved);
33774 </code></pre>
33775  * @constructor
33776  * Create a new SplitBar
33777  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33778  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33779  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33780  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33781                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33782                         position of the SplitBar).
33783  */
33784 Roo.bootstrap.SplitBar = function(cfg){
33785     
33786     /** @private */
33787     
33788     //{
33789     //  dragElement : elm
33790     //  resizingElement: el,
33791         // optional..
33792     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33793     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33794         // existingProxy ???
33795     //}
33796     
33797     this.el = Roo.get(cfg.dragElement, true);
33798     this.el.dom.unselectable = "on";
33799     /** @private */
33800     this.resizingEl = Roo.get(cfg.resizingElement, true);
33801
33802     /**
33803      * @private
33804      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33805      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33806      * @type Number
33807      */
33808     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33809     
33810     /**
33811      * The minimum size of the resizing element. (Defaults to 0)
33812      * @type Number
33813      */
33814     this.minSize = 0;
33815     
33816     /**
33817      * The maximum size of the resizing element. (Defaults to 2000)
33818      * @type Number
33819      */
33820     this.maxSize = 2000;
33821     
33822     /**
33823      * Whether to animate the transition to the new size
33824      * @type Boolean
33825      */
33826     this.animate = false;
33827     
33828     /**
33829      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33830      * @type Boolean
33831      */
33832     this.useShim = false;
33833     
33834     /** @private */
33835     this.shim = null;
33836     
33837     if(!cfg.existingProxy){
33838         /** @private */
33839         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33840     }else{
33841         this.proxy = Roo.get(cfg.existingProxy).dom;
33842     }
33843     /** @private */
33844     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33845     
33846     /** @private */
33847     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33848     
33849     /** @private */
33850     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33851     
33852     /** @private */
33853     this.dragSpecs = {};
33854     
33855     /**
33856      * @private The adapter to use to positon and resize elements
33857      */
33858     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33859     this.adapter.init(this);
33860     
33861     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33862         /** @private */
33863         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33864         this.el.addClass("roo-splitbar-h");
33865     }else{
33866         /** @private */
33867         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33868         this.el.addClass("roo-splitbar-v");
33869     }
33870     
33871     this.addEvents({
33872         /**
33873          * @event resize
33874          * Fires when the splitter is moved (alias for {@link #event-moved})
33875          * @param {Roo.bootstrap.SplitBar} this
33876          * @param {Number} newSize the new width or height
33877          */
33878         "resize" : true,
33879         /**
33880          * @event moved
33881          * Fires when the splitter is moved
33882          * @param {Roo.bootstrap.SplitBar} this
33883          * @param {Number} newSize the new width or height
33884          */
33885         "moved" : true,
33886         /**
33887          * @event beforeresize
33888          * Fires before the splitter is dragged
33889          * @param {Roo.bootstrap.SplitBar} this
33890          */
33891         "beforeresize" : true,
33892
33893         "beforeapply" : true
33894     });
33895
33896     Roo.util.Observable.call(this);
33897 };
33898
33899 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33900     onStartProxyDrag : function(x, y){
33901         this.fireEvent("beforeresize", this);
33902         if(!this.overlay){
33903             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33904             o.unselectable();
33905             o.enableDisplayMode("block");
33906             // all splitbars share the same overlay
33907             Roo.bootstrap.SplitBar.prototype.overlay = o;
33908         }
33909         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33910         this.overlay.show();
33911         Roo.get(this.proxy).setDisplayed("block");
33912         var size = this.adapter.getElementSize(this);
33913         this.activeMinSize = this.getMinimumSize();;
33914         this.activeMaxSize = this.getMaximumSize();;
33915         var c1 = size - this.activeMinSize;
33916         var c2 = Math.max(this.activeMaxSize - size, 0);
33917         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33918             this.dd.resetConstraints();
33919             this.dd.setXConstraint(
33920                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33921                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33922             );
33923             this.dd.setYConstraint(0, 0);
33924         }else{
33925             this.dd.resetConstraints();
33926             this.dd.setXConstraint(0, 0);
33927             this.dd.setYConstraint(
33928                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33929                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33930             );
33931          }
33932         this.dragSpecs.startSize = size;
33933         this.dragSpecs.startPoint = [x, y];
33934         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33935     },
33936     
33937     /** 
33938      * @private Called after the drag operation by the DDProxy
33939      */
33940     onEndProxyDrag : function(e){
33941         Roo.get(this.proxy).setDisplayed(false);
33942         var endPoint = Roo.lib.Event.getXY(e);
33943         if(this.overlay){
33944             this.overlay.hide();
33945         }
33946         var newSize;
33947         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33948             newSize = this.dragSpecs.startSize + 
33949                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33950                     endPoint[0] - this.dragSpecs.startPoint[0] :
33951                     this.dragSpecs.startPoint[0] - endPoint[0]
33952                 );
33953         }else{
33954             newSize = this.dragSpecs.startSize + 
33955                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33956                     endPoint[1] - this.dragSpecs.startPoint[1] :
33957                     this.dragSpecs.startPoint[1] - endPoint[1]
33958                 );
33959         }
33960         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33961         if(newSize != this.dragSpecs.startSize){
33962             if(this.fireEvent('beforeapply', this, newSize) !== false){
33963                 this.adapter.setElementSize(this, newSize);
33964                 this.fireEvent("moved", this, newSize);
33965                 this.fireEvent("resize", this, newSize);
33966             }
33967         }
33968     },
33969     
33970     /**
33971      * Get the adapter this SplitBar uses
33972      * @return The adapter object
33973      */
33974     getAdapter : function(){
33975         return this.adapter;
33976     },
33977     
33978     /**
33979      * Set the adapter this SplitBar uses
33980      * @param {Object} adapter A SplitBar adapter object
33981      */
33982     setAdapter : function(adapter){
33983         this.adapter = adapter;
33984         this.adapter.init(this);
33985     },
33986     
33987     /**
33988      * Gets the minimum size for the resizing element
33989      * @return {Number} The minimum size
33990      */
33991     getMinimumSize : function(){
33992         return this.minSize;
33993     },
33994     
33995     /**
33996      * Sets the minimum size for the resizing element
33997      * @param {Number} minSize The minimum size
33998      */
33999     setMinimumSize : function(minSize){
34000         this.minSize = minSize;
34001     },
34002     
34003     /**
34004      * Gets the maximum size for the resizing element
34005      * @return {Number} The maximum size
34006      */
34007     getMaximumSize : function(){
34008         return this.maxSize;
34009     },
34010     
34011     /**
34012      * Sets the maximum size for the resizing element
34013      * @param {Number} maxSize The maximum size
34014      */
34015     setMaximumSize : function(maxSize){
34016         this.maxSize = maxSize;
34017     },
34018     
34019     /**
34020      * Sets the initialize size for the resizing element
34021      * @param {Number} size The initial size
34022      */
34023     setCurrentSize : function(size){
34024         var oldAnimate = this.animate;
34025         this.animate = false;
34026         this.adapter.setElementSize(this, size);
34027         this.animate = oldAnimate;
34028     },
34029     
34030     /**
34031      * Destroy this splitbar. 
34032      * @param {Boolean} removeEl True to remove the element
34033      */
34034     destroy : function(removeEl){
34035         if(this.shim){
34036             this.shim.remove();
34037         }
34038         this.dd.unreg();
34039         this.proxy.parentNode.removeChild(this.proxy);
34040         if(removeEl){
34041             this.el.remove();
34042         }
34043     }
34044 });
34045
34046 /**
34047  * @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.
34048  */
34049 Roo.bootstrap.SplitBar.createProxy = function(dir){
34050     var proxy = new Roo.Element(document.createElement("div"));
34051     proxy.unselectable();
34052     var cls = 'roo-splitbar-proxy';
34053     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34054     document.body.appendChild(proxy.dom);
34055     return proxy.dom;
34056 };
34057
34058 /** 
34059  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34060  * Default Adapter. It assumes the splitter and resizing element are not positioned
34061  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34062  */
34063 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34064 };
34065
34066 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34067     // do nothing for now
34068     init : function(s){
34069     
34070     },
34071     /**
34072      * Called before drag operations to get the current size of the resizing element. 
34073      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34074      */
34075      getElementSize : function(s){
34076         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34077             return s.resizingEl.getWidth();
34078         }else{
34079             return s.resizingEl.getHeight();
34080         }
34081     },
34082     
34083     /**
34084      * Called after drag operations to set the size of the resizing element.
34085      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34086      * @param {Number} newSize The new size to set
34087      * @param {Function} onComplete A function to be invoked when resizing is complete
34088      */
34089     setElementSize : function(s, newSize, onComplete){
34090         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34091             if(!s.animate){
34092                 s.resizingEl.setWidth(newSize);
34093                 if(onComplete){
34094                     onComplete(s, newSize);
34095                 }
34096             }else{
34097                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34098             }
34099         }else{
34100             
34101             if(!s.animate){
34102                 s.resizingEl.setHeight(newSize);
34103                 if(onComplete){
34104                     onComplete(s, newSize);
34105                 }
34106             }else{
34107                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34108             }
34109         }
34110     }
34111 };
34112
34113 /** 
34114  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34115  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34116  * Adapter that  moves the splitter element to align with the resized sizing element. 
34117  * Used with an absolute positioned SplitBar.
34118  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34119  * document.body, make sure you assign an id to the body element.
34120  */
34121 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34122     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34123     this.container = Roo.get(container);
34124 };
34125
34126 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34127     init : function(s){
34128         this.basic.init(s);
34129     },
34130     
34131     getElementSize : function(s){
34132         return this.basic.getElementSize(s);
34133     },
34134     
34135     setElementSize : function(s, newSize, onComplete){
34136         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34137     },
34138     
34139     moveSplitter : function(s){
34140         var yes = Roo.bootstrap.SplitBar;
34141         switch(s.placement){
34142             case yes.LEFT:
34143                 s.el.setX(s.resizingEl.getRight());
34144                 break;
34145             case yes.RIGHT:
34146                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34147                 break;
34148             case yes.TOP:
34149                 s.el.setY(s.resizingEl.getBottom());
34150                 break;
34151             case yes.BOTTOM:
34152                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34153                 break;
34154         }
34155     }
34156 };
34157
34158 /**
34159  * Orientation constant - Create a vertical SplitBar
34160  * @static
34161  * @type Number
34162  */
34163 Roo.bootstrap.SplitBar.VERTICAL = 1;
34164
34165 /**
34166  * Orientation constant - Create a horizontal SplitBar
34167  * @static
34168  * @type Number
34169  */
34170 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34171
34172 /**
34173  * Placement constant - The resizing element is to the left of the splitter element
34174  * @static
34175  * @type Number
34176  */
34177 Roo.bootstrap.SplitBar.LEFT = 1;
34178
34179 /**
34180  * Placement constant - The resizing element is to the right of the splitter element
34181  * @static
34182  * @type Number
34183  */
34184 Roo.bootstrap.SplitBar.RIGHT = 2;
34185
34186 /**
34187  * Placement constant - The resizing element is positioned above the splitter element
34188  * @static
34189  * @type Number
34190  */
34191 Roo.bootstrap.SplitBar.TOP = 3;
34192
34193 /**
34194  * Placement constant - The resizing element is positioned under splitter element
34195  * @static
34196  * @type Number
34197  */
34198 Roo.bootstrap.SplitBar.BOTTOM = 4;
34199 Roo.namespace("Roo.bootstrap.layout");/*
34200  * Based on:
34201  * Ext JS Library 1.1.1
34202  * Copyright(c) 2006-2007, Ext JS, LLC.
34203  *
34204  * Originally Released Under LGPL - original licence link has changed is not relivant.
34205  *
34206  * Fork - LGPL
34207  * <script type="text/javascript">
34208  */
34209
34210 /**
34211  * @class Roo.bootstrap.layout.Manager
34212  * @extends Roo.bootstrap.Component
34213  * Base class for layout managers.
34214  */
34215 Roo.bootstrap.layout.Manager = function(config)
34216 {
34217     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34218
34219
34220
34221
34222
34223     /** false to disable window resize monitoring @type Boolean */
34224     this.monitorWindowResize = true;
34225     this.regions = {};
34226     this.addEvents({
34227         /**
34228          * @event layout
34229          * Fires when a layout is performed.
34230          * @param {Roo.LayoutManager} this
34231          */
34232         "layout" : true,
34233         /**
34234          * @event regionresized
34235          * Fires when the user resizes a region.
34236          * @param {Roo.LayoutRegion} region The resized region
34237          * @param {Number} newSize The new size (width for east/west, height for north/south)
34238          */
34239         "regionresized" : true,
34240         /**
34241          * @event regioncollapsed
34242          * Fires when a region is collapsed.
34243          * @param {Roo.LayoutRegion} region The collapsed region
34244          */
34245         "regioncollapsed" : true,
34246         /**
34247          * @event regionexpanded
34248          * Fires when a region is expanded.
34249          * @param {Roo.LayoutRegion} region The expanded region
34250          */
34251         "regionexpanded" : true
34252     });
34253     this.updating = false;
34254
34255     if (config.el) {
34256         this.el = Roo.get(config.el);
34257         this.initEvents();
34258     }
34259
34260 };
34261
34262 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34263
34264
34265     regions : null,
34266
34267     monitorWindowResize : true,
34268
34269
34270     updating : false,
34271
34272
34273     onRender : function(ct, position)
34274     {
34275         if(!this.el){
34276             this.el = Roo.get(ct);
34277             this.initEvents();
34278         }
34279         //this.fireEvent('render',this);
34280     },
34281
34282
34283     initEvents: function()
34284     {
34285
34286
34287         // ie scrollbar fix
34288         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34289             document.body.scroll = "no";
34290         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34291             this.el.position('relative');
34292         }
34293         this.id = this.el.id;
34294         this.el.addClass("roo-layout-container");
34295         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34296         if(this.el.dom != document.body ) {
34297             this.el.on('resize', this.layout,this);
34298             this.el.on('show', this.layout,this);
34299         }
34300
34301     },
34302
34303     /**
34304      * Returns true if this layout is currently being updated
34305      * @return {Boolean}
34306      */
34307     isUpdating : function(){
34308         return this.updating;
34309     },
34310
34311     /**
34312      * Suspend the LayoutManager from doing auto-layouts while
34313      * making multiple add or remove calls
34314      */
34315     beginUpdate : function(){
34316         this.updating = true;
34317     },
34318
34319     /**
34320      * Restore auto-layouts and optionally disable the manager from performing a layout
34321      * @param {Boolean} noLayout true to disable a layout update
34322      */
34323     endUpdate : function(noLayout){
34324         this.updating = false;
34325         if(!noLayout){
34326             this.layout();
34327         }
34328     },
34329
34330     layout: function(){
34331         // abstract...
34332     },
34333
34334     onRegionResized : function(region, newSize){
34335         this.fireEvent("regionresized", region, newSize);
34336         this.layout();
34337     },
34338
34339     onRegionCollapsed : function(region){
34340         this.fireEvent("regioncollapsed", region);
34341     },
34342
34343     onRegionExpanded : function(region){
34344         this.fireEvent("regionexpanded", region);
34345     },
34346
34347     /**
34348      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34349      * performs box-model adjustments.
34350      * @return {Object} The size as an object {width: (the width), height: (the height)}
34351      */
34352     getViewSize : function()
34353     {
34354         var size;
34355         if(this.el.dom != document.body){
34356             size = this.el.getSize();
34357         }else{
34358             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34359         }
34360         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34361         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34362         return size;
34363     },
34364
34365     /**
34366      * Returns the Element this layout is bound to.
34367      * @return {Roo.Element}
34368      */
34369     getEl : function(){
34370         return this.el;
34371     },
34372
34373     /**
34374      * Returns the specified region.
34375      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34376      * @return {Roo.LayoutRegion}
34377      */
34378     getRegion : function(target){
34379         return this.regions[target.toLowerCase()];
34380     },
34381
34382     onWindowResize : function(){
34383         if(this.monitorWindowResize){
34384             this.layout();
34385         }
34386     }
34387 });
34388 /*
34389  * Based on:
34390  * Ext JS Library 1.1.1
34391  * Copyright(c) 2006-2007, Ext JS, LLC.
34392  *
34393  * Originally Released Under LGPL - original licence link has changed is not relivant.
34394  *
34395  * Fork - LGPL
34396  * <script type="text/javascript">
34397  */
34398 /**
34399  * @class Roo.bootstrap.layout.Border
34400  * @extends Roo.bootstrap.layout.Manager
34401  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34402  * please see: examples/bootstrap/nested.html<br><br>
34403  
34404 <b>The container the layout is rendered into can be either the body element or any other element.
34405 If it is not the body element, the container needs to either be an absolute positioned element,
34406 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34407 the container size if it is not the body element.</b>
34408
34409 * @constructor
34410 * Create a new Border
34411 * @param {Object} config Configuration options
34412  */
34413 Roo.bootstrap.layout.Border = function(config){
34414     config = config || {};
34415     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34416     
34417     
34418     
34419     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34420         if(config[region]){
34421             config[region].region = region;
34422             this.addRegion(config[region]);
34423         }
34424     },this);
34425     
34426 };
34427
34428 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34429
34430 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34431     /**
34432      * Creates and adds a new region if it doesn't already exist.
34433      * @param {String} target The target region key (north, south, east, west or center).
34434      * @param {Object} config The regions config object
34435      * @return {BorderLayoutRegion} The new region
34436      */
34437     addRegion : function(config)
34438     {
34439         if(!this.regions[config.region]){
34440             var r = this.factory(config);
34441             this.bindRegion(r);
34442         }
34443         return this.regions[config.region];
34444     },
34445
34446     // private (kinda)
34447     bindRegion : function(r){
34448         this.regions[r.config.region] = r;
34449         
34450         r.on("visibilitychange",    this.layout, this);
34451         r.on("paneladded",          this.layout, this);
34452         r.on("panelremoved",        this.layout, this);
34453         r.on("invalidated",         this.layout, this);
34454         r.on("resized",             this.onRegionResized, this);
34455         r.on("collapsed",           this.onRegionCollapsed, this);
34456         r.on("expanded",            this.onRegionExpanded, this);
34457     },
34458
34459     /**
34460      * Performs a layout update.
34461      */
34462     layout : function()
34463     {
34464         if(this.updating) {
34465             return;
34466         }
34467         
34468         // render all the rebions if they have not been done alreayd?
34469         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34470             if(this.regions[region] && !this.regions[region].bodyEl){
34471                 this.regions[region].onRender(this.el)
34472             }
34473         },this);
34474         
34475         var size = this.getViewSize();
34476         var w = size.width;
34477         var h = size.height;
34478         var centerW = w;
34479         var centerH = h;
34480         var centerY = 0;
34481         var centerX = 0;
34482         //var x = 0, y = 0;
34483
34484         var rs = this.regions;
34485         var north = rs["north"];
34486         var south = rs["south"]; 
34487         var west = rs["west"];
34488         var east = rs["east"];
34489         var center = rs["center"];
34490         //if(this.hideOnLayout){ // not supported anymore
34491             //c.el.setStyle("display", "none");
34492         //}
34493         if(north && north.isVisible()){
34494             var b = north.getBox();
34495             var m = north.getMargins();
34496             b.width = w - (m.left+m.right);
34497             b.x = m.left;
34498             b.y = m.top;
34499             centerY = b.height + b.y + m.bottom;
34500             centerH -= centerY;
34501             north.updateBox(this.safeBox(b));
34502         }
34503         if(south && south.isVisible()){
34504             var b = south.getBox();
34505             var m = south.getMargins();
34506             b.width = w - (m.left+m.right);
34507             b.x = m.left;
34508             var totalHeight = (b.height + m.top + m.bottom);
34509             b.y = h - totalHeight + m.top;
34510             centerH -= totalHeight;
34511             south.updateBox(this.safeBox(b));
34512         }
34513         if(west && west.isVisible()){
34514             var b = west.getBox();
34515             var m = west.getMargins();
34516             b.height = centerH - (m.top+m.bottom);
34517             b.x = m.left;
34518             b.y = centerY + m.top;
34519             var totalWidth = (b.width + m.left + m.right);
34520             centerX += totalWidth;
34521             centerW -= totalWidth;
34522             west.updateBox(this.safeBox(b));
34523         }
34524         if(east && east.isVisible()){
34525             var b = east.getBox();
34526             var m = east.getMargins();
34527             b.height = centerH - (m.top+m.bottom);
34528             var totalWidth = (b.width + m.left + m.right);
34529             b.x = w - totalWidth + m.left;
34530             b.y = centerY + m.top;
34531             centerW -= totalWidth;
34532             east.updateBox(this.safeBox(b));
34533         }
34534         if(center){
34535             var m = center.getMargins();
34536             var centerBox = {
34537                 x: centerX + m.left,
34538                 y: centerY + m.top,
34539                 width: centerW - (m.left+m.right),
34540                 height: centerH - (m.top+m.bottom)
34541             };
34542             //if(this.hideOnLayout){
34543                 //center.el.setStyle("display", "block");
34544             //}
34545             center.updateBox(this.safeBox(centerBox));
34546         }
34547         this.el.repaint();
34548         this.fireEvent("layout", this);
34549     },
34550
34551     // private
34552     safeBox : function(box){
34553         box.width = Math.max(0, box.width);
34554         box.height = Math.max(0, box.height);
34555         return box;
34556     },
34557
34558     /**
34559      * Adds a ContentPanel (or subclass) to this layout.
34560      * @param {String} target The target region key (north, south, east, west or center).
34561      * @param {Roo.ContentPanel} panel The panel to add
34562      * @return {Roo.ContentPanel} The added panel
34563      */
34564     add : function(target, panel){
34565          
34566         target = target.toLowerCase();
34567         return this.regions[target].add(panel);
34568     },
34569
34570     /**
34571      * Remove a ContentPanel (or subclass) to this layout.
34572      * @param {String} target The target region key (north, south, east, west or center).
34573      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34574      * @return {Roo.ContentPanel} The removed panel
34575      */
34576     remove : function(target, panel){
34577         target = target.toLowerCase();
34578         return this.regions[target].remove(panel);
34579     },
34580
34581     /**
34582      * Searches all regions for a panel with the specified id
34583      * @param {String} panelId
34584      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34585      */
34586     findPanel : function(panelId){
34587         var rs = this.regions;
34588         for(var target in rs){
34589             if(typeof rs[target] != "function"){
34590                 var p = rs[target].getPanel(panelId);
34591                 if(p){
34592                     return p;
34593                 }
34594             }
34595         }
34596         return null;
34597     },
34598
34599     /**
34600      * Searches all regions for a panel with the specified id and activates (shows) it.
34601      * @param {String/ContentPanel} panelId The panels id or the panel itself
34602      * @return {Roo.ContentPanel} The shown panel or null
34603      */
34604     showPanel : function(panelId) {
34605       var rs = this.regions;
34606       for(var target in rs){
34607          var r = rs[target];
34608          if(typeof r != "function"){
34609             if(r.hasPanel(panelId)){
34610                return r.showPanel(panelId);
34611             }
34612          }
34613       }
34614       return null;
34615    },
34616
34617    /**
34618      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34619      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34620      */
34621    /*
34622     restoreState : function(provider){
34623         if(!provider){
34624             provider = Roo.state.Manager;
34625         }
34626         var sm = new Roo.LayoutStateManager();
34627         sm.init(this, provider);
34628     },
34629 */
34630  
34631  
34632     /**
34633      * Adds a xtype elements to the layout.
34634      * <pre><code>
34635
34636 layout.addxtype({
34637        xtype : 'ContentPanel',
34638        region: 'west',
34639        items: [ .... ]
34640    }
34641 );
34642
34643 layout.addxtype({
34644         xtype : 'NestedLayoutPanel',
34645         region: 'west',
34646         layout: {
34647            center: { },
34648            west: { }   
34649         },
34650         items : [ ... list of content panels or nested layout panels.. ]
34651    }
34652 );
34653 </code></pre>
34654      * @param {Object} cfg Xtype definition of item to add.
34655      */
34656     addxtype : function(cfg)
34657     {
34658         // basically accepts a pannel...
34659         // can accept a layout region..!?!?
34660         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34661         
34662         
34663         // theory?  children can only be panels??
34664         
34665         //if (!cfg.xtype.match(/Panel$/)) {
34666         //    return false;
34667         //}
34668         var ret = false;
34669         
34670         if (typeof(cfg.region) == 'undefined') {
34671             Roo.log("Failed to add Panel, region was not set");
34672             Roo.log(cfg);
34673             return false;
34674         }
34675         var region = cfg.region;
34676         delete cfg.region;
34677         
34678           
34679         var xitems = [];
34680         if (cfg.items) {
34681             xitems = cfg.items;
34682             delete cfg.items;
34683         }
34684         var nb = false;
34685         
34686         switch(cfg.xtype) 
34687         {
34688             case 'Content':  // ContentPanel (el, cfg)
34689             case 'Scroll':  // ContentPanel (el, cfg)
34690             case 'View': 
34691                 cfg.autoCreate = true;
34692                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34693                 //} else {
34694                 //    var el = this.el.createChild();
34695                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34696                 //}
34697                 
34698                 this.add(region, ret);
34699                 break;
34700             
34701             /*
34702             case 'TreePanel': // our new panel!
34703                 cfg.el = this.el.createChild();
34704                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34705                 this.add(region, ret);
34706                 break;
34707             */
34708             
34709             case 'Nest': 
34710                 // create a new Layout (which is  a Border Layout...
34711                 
34712                 var clayout = cfg.layout;
34713                 clayout.el  = this.el.createChild();
34714                 clayout.items   = clayout.items  || [];
34715                 
34716                 delete cfg.layout;
34717                 
34718                 // replace this exitems with the clayout ones..
34719                 xitems = clayout.items;
34720                  
34721                 // force background off if it's in center...
34722                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34723                     cfg.background = false;
34724                 }
34725                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34726                 
34727                 
34728                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34729                 //console.log('adding nested layout panel '  + cfg.toSource());
34730                 this.add(region, ret);
34731                 nb = {}; /// find first...
34732                 break;
34733             
34734             case 'Grid':
34735                 
34736                 // needs grid and region
34737                 
34738                 //var el = this.getRegion(region).el.createChild();
34739                 /*
34740                  *var el = this.el.createChild();
34741                 // create the grid first...
34742                 cfg.grid.container = el;
34743                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34744                 */
34745                 
34746                 if (region == 'center' && this.active ) {
34747                     cfg.background = false;
34748                 }
34749                 
34750                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34751                 
34752                 this.add(region, ret);
34753                 /*
34754                 if (cfg.background) {
34755                     // render grid on panel activation (if panel background)
34756                     ret.on('activate', function(gp) {
34757                         if (!gp.grid.rendered) {
34758                     //        gp.grid.render(el);
34759                         }
34760                     });
34761                 } else {
34762                   //  cfg.grid.render(el);
34763                 }
34764                 */
34765                 break;
34766            
34767            
34768             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34769                 // it was the old xcomponent building that caused this before.
34770                 // espeically if border is the top element in the tree.
34771                 ret = this;
34772                 break; 
34773                 
34774                     
34775                 
34776                 
34777                 
34778             default:
34779                 /*
34780                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34781                     
34782                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34783                     this.add(region, ret);
34784                 } else {
34785                 */
34786                     Roo.log(cfg);
34787                     throw "Can not add '" + cfg.xtype + "' to Border";
34788                     return null;
34789              
34790                                 
34791              
34792         }
34793         this.beginUpdate();
34794         // add children..
34795         var region = '';
34796         var abn = {};
34797         Roo.each(xitems, function(i)  {
34798             region = nb && i.region ? i.region : false;
34799             
34800             var add = ret.addxtype(i);
34801            
34802             if (region) {
34803                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34804                 if (!i.background) {
34805                     abn[region] = nb[region] ;
34806                 }
34807             }
34808             
34809         });
34810         this.endUpdate();
34811
34812         // make the last non-background panel active..
34813         //if (nb) { Roo.log(abn); }
34814         if (nb) {
34815             
34816             for(var r in abn) {
34817                 region = this.getRegion(r);
34818                 if (region) {
34819                     // tried using nb[r], but it does not work..
34820                      
34821                     region.showPanel(abn[r]);
34822                    
34823                 }
34824             }
34825         }
34826         return ret;
34827         
34828     },
34829     
34830     
34831 // private
34832     factory : function(cfg)
34833     {
34834         
34835         var validRegions = Roo.bootstrap.layout.Border.regions;
34836
34837         var target = cfg.region;
34838         cfg.mgr = this;
34839         
34840         var r = Roo.bootstrap.layout;
34841         Roo.log(target);
34842         switch(target){
34843             case "north":
34844                 return new r.North(cfg);
34845             case "south":
34846                 return new r.South(cfg);
34847             case "east":
34848                 return new r.East(cfg);
34849             case "west":
34850                 return new r.West(cfg);
34851             case "center":
34852                 return new r.Center(cfg);
34853         }
34854         throw 'Layout region "'+target+'" not supported.';
34855     }
34856     
34857     
34858 });
34859  /*
34860  * Based on:
34861  * Ext JS Library 1.1.1
34862  * Copyright(c) 2006-2007, Ext JS, LLC.
34863  *
34864  * Originally Released Under LGPL - original licence link has changed is not relivant.
34865  *
34866  * Fork - LGPL
34867  * <script type="text/javascript">
34868  */
34869  
34870 /**
34871  * @class Roo.bootstrap.layout.Basic
34872  * @extends Roo.util.Observable
34873  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34874  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34875  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34876  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34877  * @cfg {string}   region  the region that it inhabits..
34878  * @cfg {bool}   skipConfig skip config?
34879  * 
34880
34881  */
34882 Roo.bootstrap.layout.Basic = function(config){
34883     
34884     this.mgr = config.mgr;
34885     
34886     this.position = config.region;
34887     
34888     var skipConfig = config.skipConfig;
34889     
34890     this.events = {
34891         /**
34892          * @scope Roo.BasicLayoutRegion
34893          */
34894         
34895         /**
34896          * @event beforeremove
34897          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34898          * @param {Roo.LayoutRegion} this
34899          * @param {Roo.ContentPanel} panel The panel
34900          * @param {Object} e The cancel event object
34901          */
34902         "beforeremove" : true,
34903         /**
34904          * @event invalidated
34905          * Fires when the layout for this region is changed.
34906          * @param {Roo.LayoutRegion} this
34907          */
34908         "invalidated" : true,
34909         /**
34910          * @event visibilitychange
34911          * Fires when this region is shown or hidden 
34912          * @param {Roo.LayoutRegion} this
34913          * @param {Boolean} visibility true or false
34914          */
34915         "visibilitychange" : true,
34916         /**
34917          * @event paneladded
34918          * Fires when a panel is added. 
34919          * @param {Roo.LayoutRegion} this
34920          * @param {Roo.ContentPanel} panel The panel
34921          */
34922         "paneladded" : true,
34923         /**
34924          * @event panelremoved
34925          * Fires when a panel is removed. 
34926          * @param {Roo.LayoutRegion} this
34927          * @param {Roo.ContentPanel} panel The panel
34928          */
34929         "panelremoved" : true,
34930         /**
34931          * @event beforecollapse
34932          * Fires when this region before collapse.
34933          * @param {Roo.LayoutRegion} this
34934          */
34935         "beforecollapse" : true,
34936         /**
34937          * @event collapsed
34938          * Fires when this region is collapsed.
34939          * @param {Roo.LayoutRegion} this
34940          */
34941         "collapsed" : true,
34942         /**
34943          * @event expanded
34944          * Fires when this region is expanded.
34945          * @param {Roo.LayoutRegion} this
34946          */
34947         "expanded" : true,
34948         /**
34949          * @event slideshow
34950          * Fires when this region is slid into view.
34951          * @param {Roo.LayoutRegion} this
34952          */
34953         "slideshow" : true,
34954         /**
34955          * @event slidehide
34956          * Fires when this region slides out of view. 
34957          * @param {Roo.LayoutRegion} this
34958          */
34959         "slidehide" : true,
34960         /**
34961          * @event panelactivated
34962          * Fires when a panel is activated. 
34963          * @param {Roo.LayoutRegion} this
34964          * @param {Roo.ContentPanel} panel The activated panel
34965          */
34966         "panelactivated" : true,
34967         /**
34968          * @event resized
34969          * Fires when the user resizes this region. 
34970          * @param {Roo.LayoutRegion} this
34971          * @param {Number} newSize The new size (width for east/west, height for north/south)
34972          */
34973         "resized" : true
34974     };
34975     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34976     this.panels = new Roo.util.MixedCollection();
34977     this.panels.getKey = this.getPanelId.createDelegate(this);
34978     this.box = null;
34979     this.activePanel = null;
34980     // ensure listeners are added...
34981     
34982     if (config.listeners || config.events) {
34983         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34984             listeners : config.listeners || {},
34985             events : config.events || {}
34986         });
34987     }
34988     
34989     if(skipConfig !== true){
34990         this.applyConfig(config);
34991     }
34992 };
34993
34994 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34995 {
34996     getPanelId : function(p){
34997         return p.getId();
34998     },
34999     
35000     applyConfig : function(config){
35001         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35002         this.config = config;
35003         
35004     },
35005     
35006     /**
35007      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35008      * the width, for horizontal (north, south) the height.
35009      * @param {Number} newSize The new width or height
35010      */
35011     resizeTo : function(newSize){
35012         var el = this.el ? this.el :
35013                  (this.activePanel ? this.activePanel.getEl() : null);
35014         if(el){
35015             switch(this.position){
35016                 case "east":
35017                 case "west":
35018                     el.setWidth(newSize);
35019                     this.fireEvent("resized", this, newSize);
35020                 break;
35021                 case "north":
35022                 case "south":
35023                     el.setHeight(newSize);
35024                     this.fireEvent("resized", this, newSize);
35025                 break;                
35026             }
35027         }
35028     },
35029     
35030     getBox : function(){
35031         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35032     },
35033     
35034     getMargins : function(){
35035         return this.margins;
35036     },
35037     
35038     updateBox : function(box){
35039         this.box = box;
35040         var el = this.activePanel.getEl();
35041         el.dom.style.left = box.x + "px";
35042         el.dom.style.top = box.y + "px";
35043         this.activePanel.setSize(box.width, box.height);
35044     },
35045     
35046     /**
35047      * Returns the container element for this region.
35048      * @return {Roo.Element}
35049      */
35050     getEl : function(){
35051         return this.activePanel;
35052     },
35053     
35054     /**
35055      * Returns true if this region is currently visible.
35056      * @return {Boolean}
35057      */
35058     isVisible : function(){
35059         return this.activePanel ? true : false;
35060     },
35061     
35062     setActivePanel : function(panel){
35063         panel = this.getPanel(panel);
35064         if(this.activePanel && this.activePanel != panel){
35065             this.activePanel.setActiveState(false);
35066             this.activePanel.getEl().setLeftTop(-10000,-10000);
35067         }
35068         this.activePanel = panel;
35069         panel.setActiveState(true);
35070         if(this.box){
35071             panel.setSize(this.box.width, this.box.height);
35072         }
35073         this.fireEvent("panelactivated", this, panel);
35074         this.fireEvent("invalidated");
35075     },
35076     
35077     /**
35078      * Show the specified panel.
35079      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35080      * @return {Roo.ContentPanel} The shown panel or null
35081      */
35082     showPanel : function(panel){
35083         panel = this.getPanel(panel);
35084         if(panel){
35085             this.setActivePanel(panel);
35086         }
35087         return panel;
35088     },
35089     
35090     /**
35091      * Get the active panel for this region.
35092      * @return {Roo.ContentPanel} The active panel or null
35093      */
35094     getActivePanel : function(){
35095         return this.activePanel;
35096     },
35097     
35098     /**
35099      * Add the passed ContentPanel(s)
35100      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35101      * @return {Roo.ContentPanel} The panel added (if only one was added)
35102      */
35103     add : function(panel){
35104         if(arguments.length > 1){
35105             for(var i = 0, len = arguments.length; i < len; i++) {
35106                 this.add(arguments[i]);
35107             }
35108             return null;
35109         }
35110         if(this.hasPanel(panel)){
35111             this.showPanel(panel);
35112             return panel;
35113         }
35114         var el = panel.getEl();
35115         if(el.dom.parentNode != this.mgr.el.dom){
35116             this.mgr.el.dom.appendChild(el.dom);
35117         }
35118         if(panel.setRegion){
35119             panel.setRegion(this);
35120         }
35121         this.panels.add(panel);
35122         el.setStyle("position", "absolute");
35123         if(!panel.background){
35124             this.setActivePanel(panel);
35125             if(this.config.initialSize && this.panels.getCount()==1){
35126                 this.resizeTo(this.config.initialSize);
35127             }
35128         }
35129         this.fireEvent("paneladded", this, panel);
35130         return panel;
35131     },
35132     
35133     /**
35134      * Returns true if the panel is in this region.
35135      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35136      * @return {Boolean}
35137      */
35138     hasPanel : function(panel){
35139         if(typeof panel == "object"){ // must be panel obj
35140             panel = panel.getId();
35141         }
35142         return this.getPanel(panel) ? true : false;
35143     },
35144     
35145     /**
35146      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35148      * @param {Boolean} preservePanel Overrides the config preservePanel option
35149      * @return {Roo.ContentPanel} The panel that was removed
35150      */
35151     remove : function(panel, preservePanel){
35152         panel = this.getPanel(panel);
35153         if(!panel){
35154             return null;
35155         }
35156         var e = {};
35157         this.fireEvent("beforeremove", this, panel, e);
35158         if(e.cancel === true){
35159             return null;
35160         }
35161         var panelId = panel.getId();
35162         this.panels.removeKey(panelId);
35163         return panel;
35164     },
35165     
35166     /**
35167      * Returns the panel specified or null if it's not in this region.
35168      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35169      * @return {Roo.ContentPanel}
35170      */
35171     getPanel : function(id){
35172         if(typeof id == "object"){ // must be panel obj
35173             return id;
35174         }
35175         return this.panels.get(id);
35176     },
35177     
35178     /**
35179      * Returns this regions position (north/south/east/west/center).
35180      * @return {String} 
35181      */
35182     getPosition: function(){
35183         return this.position;    
35184     }
35185 });/*
35186  * Based on:
35187  * Ext JS Library 1.1.1
35188  * Copyright(c) 2006-2007, Ext JS, LLC.
35189  *
35190  * Originally Released Under LGPL - original licence link has changed is not relivant.
35191  *
35192  * Fork - LGPL
35193  * <script type="text/javascript">
35194  */
35195  
35196 /**
35197  * @class Roo.bootstrap.layout.Region
35198  * @extends Roo.bootstrap.layout.Basic
35199  * This class represents a region in a layout manager.
35200  
35201  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35202  * @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})
35203  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35204  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35205  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35206  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35207  * @cfg {String}    title           The title for the region (overrides panel titles)
35208  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35209  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35210  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35211  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35212  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35213  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35214  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35215  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35216  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35217  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35218
35219  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35220  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35221  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35222  * @cfg {Number}    width           For East/West panels
35223  * @cfg {Number}    height          For North/South panels
35224  * @cfg {Boolean}   split           To show the splitter
35225  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35226  * 
35227  * @cfg {string}   cls             Extra CSS classes to add to region
35228  * 
35229  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35230  * @cfg {string}   region  the region that it inhabits..
35231  *
35232
35233  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35234  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35235
35236  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35237  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35238  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35239  */
35240 Roo.bootstrap.layout.Region = function(config)
35241 {
35242     this.applyConfig(config);
35243
35244     var mgr = config.mgr;
35245     var pos = config.region;
35246     config.skipConfig = true;
35247     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35248     
35249     if (mgr.el) {
35250         this.onRender(mgr.el);   
35251     }
35252      
35253     this.visible = true;
35254     this.collapsed = false;
35255     this.unrendered_panels = [];
35256 };
35257
35258 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35259
35260     position: '', // set by wrapper (eg. north/south etc..)
35261     unrendered_panels : null,  // unrendered panels.
35262     createBody : function(){
35263         /** This region's body element 
35264         * @type Roo.Element */
35265         this.bodyEl = this.el.createChild({
35266                 tag: "div",
35267                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35268         });
35269     },
35270
35271     onRender: function(ctr, pos)
35272     {
35273         var dh = Roo.DomHelper;
35274         /** This region's container element 
35275         * @type Roo.Element */
35276         this.el = dh.append(ctr.dom, {
35277                 tag: "div",
35278                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35279             }, true);
35280         /** This region's title element 
35281         * @type Roo.Element */
35282     
35283         this.titleEl = dh.append(this.el.dom,
35284             {
35285                     tag: "div",
35286                     unselectable: "on",
35287                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35288                     children:[
35289                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35290                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35291                     ]}, true);
35292         
35293         this.titleEl.enableDisplayMode();
35294         /** This region's title text element 
35295         * @type HTMLElement */
35296         this.titleTextEl = this.titleEl.dom.firstChild;
35297         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35298         /*
35299         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35300         this.closeBtn.enableDisplayMode();
35301         this.closeBtn.on("click", this.closeClicked, this);
35302         this.closeBtn.hide();
35303     */
35304         this.createBody(this.config);
35305         if(this.config.hideWhenEmpty){
35306             this.hide();
35307             this.on("paneladded", this.validateVisibility, this);
35308             this.on("panelremoved", this.validateVisibility, this);
35309         }
35310         if(this.autoScroll){
35311             this.bodyEl.setStyle("overflow", "auto");
35312         }else{
35313             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35314         }
35315         //if(c.titlebar !== false){
35316             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35317                 this.titleEl.hide();
35318             }else{
35319                 this.titleEl.show();
35320                 if(this.config.title){
35321                     this.titleTextEl.innerHTML = this.config.title;
35322                 }
35323             }
35324         //}
35325         if(this.config.collapsed){
35326             this.collapse(true);
35327         }
35328         if(this.config.hidden){
35329             this.hide();
35330         }
35331         
35332         if (this.unrendered_panels && this.unrendered_panels.length) {
35333             for (var i =0;i< this.unrendered_panels.length; i++) {
35334                 this.add(this.unrendered_panels[i]);
35335             }
35336             this.unrendered_panels = null;
35337             
35338         }
35339         
35340     },
35341     
35342     applyConfig : function(c)
35343     {
35344         /*
35345          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35346             var dh = Roo.DomHelper;
35347             if(c.titlebar !== false){
35348                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35349                 this.collapseBtn.on("click", this.collapse, this);
35350                 this.collapseBtn.enableDisplayMode();
35351                 /*
35352                 if(c.showPin === true || this.showPin){
35353                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35354                     this.stickBtn.enableDisplayMode();
35355                     this.stickBtn.on("click", this.expand, this);
35356                     this.stickBtn.hide();
35357                 }
35358                 
35359             }
35360             */
35361             /** This region's collapsed element
35362             * @type Roo.Element */
35363             /*
35364              *
35365             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35366                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35367             ]}, true);
35368             
35369             if(c.floatable !== false){
35370                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35371                this.collapsedEl.on("click", this.collapseClick, this);
35372             }
35373
35374             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35375                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35376                    id: "message", unselectable: "on", style:{"float":"left"}});
35377                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35378              }
35379             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35380             this.expandBtn.on("click", this.expand, this);
35381             
35382         }
35383         
35384         if(this.collapseBtn){
35385             this.collapseBtn.setVisible(c.collapsible == true);
35386         }
35387         
35388         this.cmargins = c.cmargins || this.cmargins ||
35389                          (this.position == "west" || this.position == "east" ?
35390                              {top: 0, left: 2, right:2, bottom: 0} :
35391                              {top: 2, left: 0, right:0, bottom: 2});
35392         */
35393         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35394         
35395         
35396         this.bottomTabs = c.tabPosition != "top";
35397         
35398         this.autoScroll = c.autoScroll || false;
35399         
35400         
35401        
35402         
35403         this.duration = c.duration || .30;
35404         this.slideDuration = c.slideDuration || .45;
35405         this.config = c;
35406        
35407     },
35408     /**
35409      * Returns true if this region is currently visible.
35410      * @return {Boolean}
35411      */
35412     isVisible : function(){
35413         return this.visible;
35414     },
35415
35416     /**
35417      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35418      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35419      */
35420     //setCollapsedTitle : function(title){
35421     //    title = title || "&#160;";
35422      //   if(this.collapsedTitleTextEl){
35423       //      this.collapsedTitleTextEl.innerHTML = title;
35424        // }
35425     //},
35426
35427     getBox : function(){
35428         var b;
35429       //  if(!this.collapsed){
35430             b = this.el.getBox(false, true);
35431        // }else{
35432           //  b = this.collapsedEl.getBox(false, true);
35433         //}
35434         return b;
35435     },
35436
35437     getMargins : function(){
35438         return this.margins;
35439         //return this.collapsed ? this.cmargins : this.margins;
35440     },
35441 /*
35442     highlight : function(){
35443         this.el.addClass("x-layout-panel-dragover");
35444     },
35445
35446     unhighlight : function(){
35447         this.el.removeClass("x-layout-panel-dragover");
35448     },
35449 */
35450     updateBox : function(box)
35451     {
35452         if (!this.bodyEl) {
35453             return; // not rendered yet..
35454         }
35455         
35456         this.box = box;
35457         if(!this.collapsed){
35458             this.el.dom.style.left = box.x + "px";
35459             this.el.dom.style.top = box.y + "px";
35460             this.updateBody(box.width, box.height);
35461         }else{
35462             this.collapsedEl.dom.style.left = box.x + "px";
35463             this.collapsedEl.dom.style.top = box.y + "px";
35464             this.collapsedEl.setSize(box.width, box.height);
35465         }
35466         if(this.tabs){
35467             this.tabs.autoSizeTabs();
35468         }
35469     },
35470
35471     updateBody : function(w, h)
35472     {
35473         if(w !== null){
35474             this.el.setWidth(w);
35475             w -= this.el.getBorderWidth("rl");
35476             if(this.config.adjustments){
35477                 w += this.config.adjustments[0];
35478             }
35479         }
35480         if(h !== null && h > 0){
35481             this.el.setHeight(h);
35482             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35483             h -= this.el.getBorderWidth("tb");
35484             if(this.config.adjustments){
35485                 h += this.config.adjustments[1];
35486             }
35487             this.bodyEl.setHeight(h);
35488             if(this.tabs){
35489                 h = this.tabs.syncHeight(h);
35490             }
35491         }
35492         if(this.panelSize){
35493             w = w !== null ? w : this.panelSize.width;
35494             h = h !== null ? h : this.panelSize.height;
35495         }
35496         if(this.activePanel){
35497             var el = this.activePanel.getEl();
35498             w = w !== null ? w : el.getWidth();
35499             h = h !== null ? h : el.getHeight();
35500             this.panelSize = {width: w, height: h};
35501             this.activePanel.setSize(w, h);
35502         }
35503         if(Roo.isIE && this.tabs){
35504             this.tabs.el.repaint();
35505         }
35506     },
35507
35508     /**
35509      * Returns the container element for this region.
35510      * @return {Roo.Element}
35511      */
35512     getEl : function(){
35513         return this.el;
35514     },
35515
35516     /**
35517      * Hides this region.
35518      */
35519     hide : function(){
35520         //if(!this.collapsed){
35521             this.el.dom.style.left = "-2000px";
35522             this.el.hide();
35523         //}else{
35524          //   this.collapsedEl.dom.style.left = "-2000px";
35525          //   this.collapsedEl.hide();
35526        // }
35527         this.visible = false;
35528         this.fireEvent("visibilitychange", this, false);
35529     },
35530
35531     /**
35532      * Shows this region if it was previously hidden.
35533      */
35534     show : function(){
35535         //if(!this.collapsed){
35536             this.el.show();
35537         //}else{
35538         //    this.collapsedEl.show();
35539        // }
35540         this.visible = true;
35541         this.fireEvent("visibilitychange", this, true);
35542     },
35543 /*
35544     closeClicked : function(){
35545         if(this.activePanel){
35546             this.remove(this.activePanel);
35547         }
35548     },
35549
35550     collapseClick : function(e){
35551         if(this.isSlid){
35552            e.stopPropagation();
35553            this.slideIn();
35554         }else{
35555            e.stopPropagation();
35556            this.slideOut();
35557         }
35558     },
35559 */
35560     /**
35561      * Collapses this region.
35562      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35563      */
35564     /*
35565     collapse : function(skipAnim, skipCheck = false){
35566         if(this.collapsed) {
35567             return;
35568         }
35569         
35570         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35571             
35572             this.collapsed = true;
35573             if(this.split){
35574                 this.split.el.hide();
35575             }
35576             if(this.config.animate && skipAnim !== true){
35577                 this.fireEvent("invalidated", this);
35578                 this.animateCollapse();
35579             }else{
35580                 this.el.setLocation(-20000,-20000);
35581                 this.el.hide();
35582                 this.collapsedEl.show();
35583                 this.fireEvent("collapsed", this);
35584                 this.fireEvent("invalidated", this);
35585             }
35586         }
35587         
35588     },
35589 */
35590     animateCollapse : function(){
35591         // overridden
35592     },
35593
35594     /**
35595      * Expands this region if it was previously collapsed.
35596      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35597      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35598      */
35599     /*
35600     expand : function(e, skipAnim){
35601         if(e) {
35602             e.stopPropagation();
35603         }
35604         if(!this.collapsed || this.el.hasActiveFx()) {
35605             return;
35606         }
35607         if(this.isSlid){
35608             this.afterSlideIn();
35609             skipAnim = true;
35610         }
35611         this.collapsed = false;
35612         if(this.config.animate && skipAnim !== true){
35613             this.animateExpand();
35614         }else{
35615             this.el.show();
35616             if(this.split){
35617                 this.split.el.show();
35618             }
35619             this.collapsedEl.setLocation(-2000,-2000);
35620             this.collapsedEl.hide();
35621             this.fireEvent("invalidated", this);
35622             this.fireEvent("expanded", this);
35623         }
35624     },
35625 */
35626     animateExpand : function(){
35627         // overridden
35628     },
35629
35630     initTabs : function()
35631     {
35632         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35633         
35634         var ts = new Roo.bootstrap.panel.Tabs({
35635                 el: this.bodyEl.dom,
35636                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35637                 disableTooltips: this.config.disableTabTips,
35638                 toolbar : this.config.toolbar
35639             });
35640         
35641         if(this.config.hideTabs){
35642             ts.stripWrap.setDisplayed(false);
35643         }
35644         this.tabs = ts;
35645         ts.resizeTabs = this.config.resizeTabs === true;
35646         ts.minTabWidth = this.config.minTabWidth || 40;
35647         ts.maxTabWidth = this.config.maxTabWidth || 250;
35648         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35649         ts.monitorResize = false;
35650         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35651         ts.bodyEl.addClass('roo-layout-tabs-body');
35652         this.panels.each(this.initPanelAsTab, this);
35653     },
35654
35655     initPanelAsTab : function(panel){
35656         var ti = this.tabs.addTab(
35657             panel.getEl().id,
35658             panel.getTitle(),
35659             null,
35660             this.config.closeOnTab && panel.isClosable(),
35661             panel.tpl
35662         );
35663         if(panel.tabTip !== undefined){
35664             ti.setTooltip(panel.tabTip);
35665         }
35666         ti.on("activate", function(){
35667               this.setActivePanel(panel);
35668         }, this);
35669         
35670         if(this.config.closeOnTab){
35671             ti.on("beforeclose", function(t, e){
35672                 e.cancel = true;
35673                 this.remove(panel);
35674             }, this);
35675         }
35676         
35677         panel.tabItem = ti;
35678         
35679         return ti;
35680     },
35681
35682     updatePanelTitle : function(panel, title)
35683     {
35684         if(this.activePanel == panel){
35685             this.updateTitle(title);
35686         }
35687         if(this.tabs){
35688             var ti = this.tabs.getTab(panel.getEl().id);
35689             ti.setText(title);
35690             if(panel.tabTip !== undefined){
35691                 ti.setTooltip(panel.tabTip);
35692             }
35693         }
35694     },
35695
35696     updateTitle : function(title){
35697         if(this.titleTextEl && !this.config.title){
35698             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35699         }
35700     },
35701
35702     setActivePanel : function(panel)
35703     {
35704         panel = this.getPanel(panel);
35705         if(this.activePanel && this.activePanel != panel){
35706             if(this.activePanel.setActiveState(false) === false){
35707                 return;
35708             }
35709         }
35710         this.activePanel = panel;
35711         panel.setActiveState(true);
35712         if(this.panelSize){
35713             panel.setSize(this.panelSize.width, this.panelSize.height);
35714         }
35715         if(this.closeBtn){
35716             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35717         }
35718         this.updateTitle(panel.getTitle());
35719         if(this.tabs){
35720             this.fireEvent("invalidated", this);
35721         }
35722         this.fireEvent("panelactivated", this, panel);
35723     },
35724
35725     /**
35726      * Shows the specified panel.
35727      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35728      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35729      */
35730     showPanel : function(panel)
35731     {
35732         panel = this.getPanel(panel);
35733         if(panel){
35734             if(this.tabs){
35735                 var tab = this.tabs.getTab(panel.getEl().id);
35736                 if(tab.isHidden()){
35737                     this.tabs.unhideTab(tab.id);
35738                 }
35739                 tab.activate();
35740             }else{
35741                 this.setActivePanel(panel);
35742             }
35743         }
35744         return panel;
35745     },
35746
35747     /**
35748      * Get the active panel for this region.
35749      * @return {Roo.ContentPanel} The active panel or null
35750      */
35751     getActivePanel : function(){
35752         return this.activePanel;
35753     },
35754
35755     validateVisibility : function(){
35756         if(this.panels.getCount() < 1){
35757             this.updateTitle("&#160;");
35758             this.closeBtn.hide();
35759             this.hide();
35760         }else{
35761             if(!this.isVisible()){
35762                 this.show();
35763             }
35764         }
35765     },
35766
35767     /**
35768      * Adds the passed ContentPanel(s) to this region.
35769      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35770      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35771      */
35772     add : function(panel)
35773     {
35774         if(arguments.length > 1){
35775             for(var i = 0, len = arguments.length; i < len; i++) {
35776                 this.add(arguments[i]);
35777             }
35778             return null;
35779         }
35780         
35781         // if we have not been rendered yet, then we can not really do much of this..
35782         if (!this.bodyEl) {
35783             this.unrendered_panels.push(panel);
35784             return panel;
35785         }
35786         
35787         
35788         
35789         
35790         if(this.hasPanel(panel)){
35791             this.showPanel(panel);
35792             return panel;
35793         }
35794         panel.setRegion(this);
35795         this.panels.add(panel);
35796        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35797             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35798             // and hide them... ???
35799             this.bodyEl.dom.appendChild(panel.getEl().dom);
35800             if(panel.background !== true){
35801                 this.setActivePanel(panel);
35802             }
35803             this.fireEvent("paneladded", this, panel);
35804             return panel;
35805         }
35806         */
35807         if(!this.tabs){
35808             this.initTabs();
35809         }else{
35810             this.initPanelAsTab(panel);
35811         }
35812         
35813         
35814         if(panel.background !== true){
35815             this.tabs.activate(panel.getEl().id);
35816         }
35817         this.fireEvent("paneladded", this, panel);
35818         return panel;
35819     },
35820
35821     /**
35822      * Hides the tab for the specified panel.
35823      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35824      */
35825     hidePanel : function(panel){
35826         if(this.tabs && (panel = this.getPanel(panel))){
35827             this.tabs.hideTab(panel.getEl().id);
35828         }
35829     },
35830
35831     /**
35832      * Unhides the tab for a previously hidden panel.
35833      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35834      */
35835     unhidePanel : function(panel){
35836         if(this.tabs && (panel = this.getPanel(panel))){
35837             this.tabs.unhideTab(panel.getEl().id);
35838         }
35839     },
35840
35841     clearPanels : function(){
35842         while(this.panels.getCount() > 0){
35843              this.remove(this.panels.first());
35844         }
35845     },
35846
35847     /**
35848      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35849      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35850      * @param {Boolean} preservePanel Overrides the config preservePanel option
35851      * @return {Roo.ContentPanel} The panel that was removed
35852      */
35853     remove : function(panel, preservePanel)
35854     {
35855         panel = this.getPanel(panel);
35856         if(!panel){
35857             return null;
35858         }
35859         var e = {};
35860         this.fireEvent("beforeremove", this, panel, e);
35861         if(e.cancel === true){
35862             return null;
35863         }
35864         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35865         var panelId = panel.getId();
35866         this.panels.removeKey(panelId);
35867         if(preservePanel){
35868             document.body.appendChild(panel.getEl().dom);
35869         }
35870         if(this.tabs){
35871             this.tabs.removeTab(panel.getEl().id);
35872         }else if (!preservePanel){
35873             this.bodyEl.dom.removeChild(panel.getEl().dom);
35874         }
35875         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35876             var p = this.panels.first();
35877             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35878             tempEl.appendChild(p.getEl().dom);
35879             this.bodyEl.update("");
35880             this.bodyEl.dom.appendChild(p.getEl().dom);
35881             tempEl = null;
35882             this.updateTitle(p.getTitle());
35883             this.tabs = null;
35884             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35885             this.setActivePanel(p);
35886         }
35887         panel.setRegion(null);
35888         if(this.activePanel == panel){
35889             this.activePanel = null;
35890         }
35891         if(this.config.autoDestroy !== false && preservePanel !== true){
35892             try{panel.destroy();}catch(e){}
35893         }
35894         this.fireEvent("panelremoved", this, panel);
35895         return panel;
35896     },
35897
35898     /**
35899      * Returns the TabPanel component used by this region
35900      * @return {Roo.TabPanel}
35901      */
35902     getTabs : function(){
35903         return this.tabs;
35904     },
35905
35906     createTool : function(parentEl, className){
35907         var btn = Roo.DomHelper.append(parentEl, {
35908             tag: "div",
35909             cls: "x-layout-tools-button",
35910             children: [ {
35911                 tag: "div",
35912                 cls: "roo-layout-tools-button-inner " + className,
35913                 html: "&#160;"
35914             }]
35915         }, true);
35916         btn.addClassOnOver("roo-layout-tools-button-over");
35917         return btn;
35918     }
35919 });/*
35920  * Based on:
35921  * Ext JS Library 1.1.1
35922  * Copyright(c) 2006-2007, Ext JS, LLC.
35923  *
35924  * Originally Released Under LGPL - original licence link has changed is not relivant.
35925  *
35926  * Fork - LGPL
35927  * <script type="text/javascript">
35928  */
35929  
35930
35931
35932 /**
35933  * @class Roo.SplitLayoutRegion
35934  * @extends Roo.LayoutRegion
35935  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35936  */
35937 Roo.bootstrap.layout.Split = function(config){
35938     this.cursor = config.cursor;
35939     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35940 };
35941
35942 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35943 {
35944     splitTip : "Drag to resize.",
35945     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35946     useSplitTips : false,
35947
35948     applyConfig : function(config){
35949         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35950     },
35951     
35952     onRender : function(ctr,pos) {
35953         
35954         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35955         if(!this.config.split){
35956             return;
35957         }
35958         if(!this.split){
35959             
35960             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35961                             tag: "div",
35962                             id: this.el.id + "-split",
35963                             cls: "roo-layout-split roo-layout-split-"+this.position,
35964                             html: "&#160;"
35965             });
35966             /** The SplitBar for this region 
35967             * @type Roo.SplitBar */
35968             // does not exist yet...
35969             Roo.log([this.position, this.orientation]);
35970             
35971             this.split = new Roo.bootstrap.SplitBar({
35972                 dragElement : splitEl,
35973                 resizingElement: this.el,
35974                 orientation : this.orientation
35975             });
35976             
35977             this.split.on("moved", this.onSplitMove, this);
35978             this.split.useShim = this.config.useShim === true;
35979             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35980             if(this.useSplitTips){
35981                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35982             }
35983             //if(config.collapsible){
35984             //    this.split.el.on("dblclick", this.collapse,  this);
35985             //}
35986         }
35987         if(typeof this.config.minSize != "undefined"){
35988             this.split.minSize = this.config.minSize;
35989         }
35990         if(typeof this.config.maxSize != "undefined"){
35991             this.split.maxSize = this.config.maxSize;
35992         }
35993         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35994             this.hideSplitter();
35995         }
35996         
35997     },
35998
35999     getHMaxSize : function(){
36000          var cmax = this.config.maxSize || 10000;
36001          var center = this.mgr.getRegion("center");
36002          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36003     },
36004
36005     getVMaxSize : function(){
36006          var cmax = this.config.maxSize || 10000;
36007          var center = this.mgr.getRegion("center");
36008          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36009     },
36010
36011     onSplitMove : function(split, newSize){
36012         this.fireEvent("resized", this, newSize);
36013     },
36014     
36015     /** 
36016      * Returns the {@link Roo.SplitBar} for this region.
36017      * @return {Roo.SplitBar}
36018      */
36019     getSplitBar : function(){
36020         return this.split;
36021     },
36022     
36023     hide : function(){
36024         this.hideSplitter();
36025         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36026     },
36027
36028     hideSplitter : function(){
36029         if(this.split){
36030             this.split.el.setLocation(-2000,-2000);
36031             this.split.el.hide();
36032         }
36033     },
36034
36035     show : function(){
36036         if(this.split){
36037             this.split.el.show();
36038         }
36039         Roo.bootstrap.layout.Split.superclass.show.call(this);
36040     },
36041     
36042     beforeSlide: function(){
36043         if(Roo.isGecko){// firefox overflow auto bug workaround
36044             this.bodyEl.clip();
36045             if(this.tabs) {
36046                 this.tabs.bodyEl.clip();
36047             }
36048             if(this.activePanel){
36049                 this.activePanel.getEl().clip();
36050                 
36051                 if(this.activePanel.beforeSlide){
36052                     this.activePanel.beforeSlide();
36053                 }
36054             }
36055         }
36056     },
36057     
36058     afterSlide : function(){
36059         if(Roo.isGecko){// firefox overflow auto bug workaround
36060             this.bodyEl.unclip();
36061             if(this.tabs) {
36062                 this.tabs.bodyEl.unclip();
36063             }
36064             if(this.activePanel){
36065                 this.activePanel.getEl().unclip();
36066                 if(this.activePanel.afterSlide){
36067                     this.activePanel.afterSlide();
36068                 }
36069             }
36070         }
36071     },
36072
36073     initAutoHide : function(){
36074         if(this.autoHide !== false){
36075             if(!this.autoHideHd){
36076                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36077                 this.autoHideHd = {
36078                     "mouseout": function(e){
36079                         if(!e.within(this.el, true)){
36080                             st.delay(500);
36081                         }
36082                     },
36083                     "mouseover" : function(e){
36084                         st.cancel();
36085                     },
36086                     scope : this
36087                 };
36088             }
36089             this.el.on(this.autoHideHd);
36090         }
36091     },
36092
36093     clearAutoHide : function(){
36094         if(this.autoHide !== false){
36095             this.el.un("mouseout", this.autoHideHd.mouseout);
36096             this.el.un("mouseover", this.autoHideHd.mouseover);
36097         }
36098     },
36099
36100     clearMonitor : function(){
36101         Roo.get(document).un("click", this.slideInIf, this);
36102     },
36103
36104     // these names are backwards but not changed for compat
36105     slideOut : function(){
36106         if(this.isSlid || this.el.hasActiveFx()){
36107             return;
36108         }
36109         this.isSlid = true;
36110         if(this.collapseBtn){
36111             this.collapseBtn.hide();
36112         }
36113         this.closeBtnState = this.closeBtn.getStyle('display');
36114         this.closeBtn.hide();
36115         if(this.stickBtn){
36116             this.stickBtn.show();
36117         }
36118         this.el.show();
36119         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36120         this.beforeSlide();
36121         this.el.setStyle("z-index", 10001);
36122         this.el.slideIn(this.getSlideAnchor(), {
36123             callback: function(){
36124                 this.afterSlide();
36125                 this.initAutoHide();
36126                 Roo.get(document).on("click", this.slideInIf, this);
36127                 this.fireEvent("slideshow", this);
36128             },
36129             scope: this,
36130             block: true
36131         });
36132     },
36133
36134     afterSlideIn : function(){
36135         this.clearAutoHide();
36136         this.isSlid = false;
36137         this.clearMonitor();
36138         this.el.setStyle("z-index", "");
36139         if(this.collapseBtn){
36140             this.collapseBtn.show();
36141         }
36142         this.closeBtn.setStyle('display', this.closeBtnState);
36143         if(this.stickBtn){
36144             this.stickBtn.hide();
36145         }
36146         this.fireEvent("slidehide", this);
36147     },
36148
36149     slideIn : function(cb){
36150         if(!this.isSlid || this.el.hasActiveFx()){
36151             Roo.callback(cb);
36152             return;
36153         }
36154         this.isSlid = false;
36155         this.beforeSlide();
36156         this.el.slideOut(this.getSlideAnchor(), {
36157             callback: function(){
36158                 this.el.setLeftTop(-10000, -10000);
36159                 this.afterSlide();
36160                 this.afterSlideIn();
36161                 Roo.callback(cb);
36162             },
36163             scope: this,
36164             block: true
36165         });
36166     },
36167     
36168     slideInIf : function(e){
36169         if(!e.within(this.el)){
36170             this.slideIn();
36171         }
36172     },
36173
36174     animateCollapse : function(){
36175         this.beforeSlide();
36176         this.el.setStyle("z-index", 20000);
36177         var anchor = this.getSlideAnchor();
36178         this.el.slideOut(anchor, {
36179             callback : function(){
36180                 this.el.setStyle("z-index", "");
36181                 this.collapsedEl.slideIn(anchor, {duration:.3});
36182                 this.afterSlide();
36183                 this.el.setLocation(-10000,-10000);
36184                 this.el.hide();
36185                 this.fireEvent("collapsed", this);
36186             },
36187             scope: this,
36188             block: true
36189         });
36190     },
36191
36192     animateExpand : function(){
36193         this.beforeSlide();
36194         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36195         this.el.setStyle("z-index", 20000);
36196         this.collapsedEl.hide({
36197             duration:.1
36198         });
36199         this.el.slideIn(this.getSlideAnchor(), {
36200             callback : function(){
36201                 this.el.setStyle("z-index", "");
36202                 this.afterSlide();
36203                 if(this.split){
36204                     this.split.el.show();
36205                 }
36206                 this.fireEvent("invalidated", this);
36207                 this.fireEvent("expanded", this);
36208             },
36209             scope: this,
36210             block: true
36211         });
36212     },
36213
36214     anchors : {
36215         "west" : "left",
36216         "east" : "right",
36217         "north" : "top",
36218         "south" : "bottom"
36219     },
36220
36221     sanchors : {
36222         "west" : "l",
36223         "east" : "r",
36224         "north" : "t",
36225         "south" : "b"
36226     },
36227
36228     canchors : {
36229         "west" : "tl-tr",
36230         "east" : "tr-tl",
36231         "north" : "tl-bl",
36232         "south" : "bl-tl"
36233     },
36234
36235     getAnchor : function(){
36236         return this.anchors[this.position];
36237     },
36238
36239     getCollapseAnchor : function(){
36240         return this.canchors[this.position];
36241     },
36242
36243     getSlideAnchor : function(){
36244         return this.sanchors[this.position];
36245     },
36246
36247     getAlignAdj : function(){
36248         var cm = this.cmargins;
36249         switch(this.position){
36250             case "west":
36251                 return [0, 0];
36252             break;
36253             case "east":
36254                 return [0, 0];
36255             break;
36256             case "north":
36257                 return [0, 0];
36258             break;
36259             case "south":
36260                 return [0, 0];
36261             break;
36262         }
36263     },
36264
36265     getExpandAdj : function(){
36266         var c = this.collapsedEl, cm = this.cmargins;
36267         switch(this.position){
36268             case "west":
36269                 return [-(cm.right+c.getWidth()+cm.left), 0];
36270             break;
36271             case "east":
36272                 return [cm.right+c.getWidth()+cm.left, 0];
36273             break;
36274             case "north":
36275                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36276             break;
36277             case "south":
36278                 return [0, cm.top+cm.bottom+c.getHeight()];
36279             break;
36280         }
36281     }
36282 });/*
36283  * Based on:
36284  * Ext JS Library 1.1.1
36285  * Copyright(c) 2006-2007, Ext JS, LLC.
36286  *
36287  * Originally Released Under LGPL - original licence link has changed is not relivant.
36288  *
36289  * Fork - LGPL
36290  * <script type="text/javascript">
36291  */
36292 /*
36293  * These classes are private internal classes
36294  */
36295 Roo.bootstrap.layout.Center = function(config){
36296     config.region = "center";
36297     Roo.bootstrap.layout.Region.call(this, config);
36298     this.visible = true;
36299     this.minWidth = config.minWidth || 20;
36300     this.minHeight = config.minHeight || 20;
36301 };
36302
36303 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36304     hide : function(){
36305         // center panel can't be hidden
36306     },
36307     
36308     show : function(){
36309         // center panel can't be hidden
36310     },
36311     
36312     getMinWidth: function(){
36313         return this.minWidth;
36314     },
36315     
36316     getMinHeight: function(){
36317         return this.minHeight;
36318     }
36319 });
36320
36321
36322
36323
36324  
36325
36326
36327
36328
36329
36330 Roo.bootstrap.layout.North = function(config)
36331 {
36332     config.region = 'north';
36333     config.cursor = 'n-resize';
36334     
36335     Roo.bootstrap.layout.Split.call(this, config);
36336     
36337     
36338     if(this.split){
36339         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36340         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36341         this.split.el.addClass("roo-layout-split-v");
36342     }
36343     var size = config.initialSize || config.height;
36344     if(typeof size != "undefined"){
36345         this.el.setHeight(size);
36346     }
36347 };
36348 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36349 {
36350     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36351     
36352     
36353     
36354     getBox : function(){
36355         if(this.collapsed){
36356             return this.collapsedEl.getBox();
36357         }
36358         var box = this.el.getBox();
36359         if(this.split){
36360             box.height += this.split.el.getHeight();
36361         }
36362         return box;
36363     },
36364     
36365     updateBox : function(box){
36366         if(this.split && !this.collapsed){
36367             box.height -= this.split.el.getHeight();
36368             this.split.el.setLeft(box.x);
36369             this.split.el.setTop(box.y+box.height);
36370             this.split.el.setWidth(box.width);
36371         }
36372         if(this.collapsed){
36373             this.updateBody(box.width, null);
36374         }
36375         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36376     }
36377 });
36378
36379
36380
36381
36382
36383 Roo.bootstrap.layout.South = function(config){
36384     config.region = 'south';
36385     config.cursor = 's-resize';
36386     Roo.bootstrap.layout.Split.call(this, config);
36387     if(this.split){
36388         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36389         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36390         this.split.el.addClass("roo-layout-split-v");
36391     }
36392     var size = config.initialSize || config.height;
36393     if(typeof size != "undefined"){
36394         this.el.setHeight(size);
36395     }
36396 };
36397
36398 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36399     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36400     getBox : function(){
36401         if(this.collapsed){
36402             return this.collapsedEl.getBox();
36403         }
36404         var box = this.el.getBox();
36405         if(this.split){
36406             var sh = this.split.el.getHeight();
36407             box.height += sh;
36408             box.y -= sh;
36409         }
36410         return box;
36411     },
36412     
36413     updateBox : function(box){
36414         if(this.split && !this.collapsed){
36415             var sh = this.split.el.getHeight();
36416             box.height -= sh;
36417             box.y += sh;
36418             this.split.el.setLeft(box.x);
36419             this.split.el.setTop(box.y-sh);
36420             this.split.el.setWidth(box.width);
36421         }
36422         if(this.collapsed){
36423             this.updateBody(box.width, null);
36424         }
36425         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36426     }
36427 });
36428
36429 Roo.bootstrap.layout.East = function(config){
36430     config.region = "east";
36431     config.cursor = "e-resize";
36432     Roo.bootstrap.layout.Split.call(this, config);
36433     if(this.split){
36434         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36435         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36436         this.split.el.addClass("roo-layout-split-h");
36437     }
36438     var size = config.initialSize || config.width;
36439     if(typeof size != "undefined"){
36440         this.el.setWidth(size);
36441     }
36442 };
36443 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36444     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36445     getBox : function(){
36446         if(this.collapsed){
36447             return this.collapsedEl.getBox();
36448         }
36449         var box = this.el.getBox();
36450         if(this.split){
36451             var sw = this.split.el.getWidth();
36452             box.width += sw;
36453             box.x -= sw;
36454         }
36455         return box;
36456     },
36457
36458     updateBox : function(box){
36459         if(this.split && !this.collapsed){
36460             var sw = this.split.el.getWidth();
36461             box.width -= sw;
36462             this.split.el.setLeft(box.x);
36463             this.split.el.setTop(box.y);
36464             this.split.el.setHeight(box.height);
36465             box.x += sw;
36466         }
36467         if(this.collapsed){
36468             this.updateBody(null, box.height);
36469         }
36470         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36471     }
36472 });
36473
36474 Roo.bootstrap.layout.West = function(config){
36475     config.region = "west";
36476     config.cursor = "w-resize";
36477     
36478     Roo.bootstrap.layout.Split.call(this, config);
36479     if(this.split){
36480         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36481         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36482         this.split.el.addClass("roo-layout-split-h");
36483     }
36484     
36485 };
36486 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36487     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36488     
36489     onRender: function(ctr, pos)
36490     {
36491         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36492         var size = this.config.initialSize || this.config.width;
36493         if(typeof size != "undefined"){
36494             this.el.setWidth(size);
36495         }
36496     },
36497     
36498     getBox : function(){
36499         if(this.collapsed){
36500             return this.collapsedEl.getBox();
36501         }
36502         var box = this.el.getBox();
36503         if(this.split){
36504             box.width += this.split.el.getWidth();
36505         }
36506         return box;
36507     },
36508     
36509     updateBox : function(box){
36510         if(this.split && !this.collapsed){
36511             var sw = this.split.el.getWidth();
36512             box.width -= sw;
36513             this.split.el.setLeft(box.x+box.width);
36514             this.split.el.setTop(box.y);
36515             this.split.el.setHeight(box.height);
36516         }
36517         if(this.collapsed){
36518             this.updateBody(null, box.height);
36519         }
36520         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36521     }
36522 });
36523 Roo.namespace("Roo.bootstrap.panel");/*
36524  * Based on:
36525  * Ext JS Library 1.1.1
36526  * Copyright(c) 2006-2007, Ext JS, LLC.
36527  *
36528  * Originally Released Under LGPL - original licence link has changed is not relivant.
36529  *
36530  * Fork - LGPL
36531  * <script type="text/javascript">
36532  */
36533 /**
36534  * @class Roo.ContentPanel
36535  * @extends Roo.util.Observable
36536  * A basic ContentPanel element.
36537  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36538  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36539  * @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
36540  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36541  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36542  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36543  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36544  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36545  * @cfg {String} title          The title for this panel
36546  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36547  * @cfg {String} url            Calls {@link #setUrl} with this value
36548  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36549  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36550  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36551  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36552  * @cfg {Boolean} badges render the badges
36553
36554  * @constructor
36555  * Create a new ContentPanel.
36556  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36557  * @param {String/Object} config A string to set only the title or a config object
36558  * @param {String} content (optional) Set the HTML content for this panel
36559  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36560  */
36561 Roo.bootstrap.panel.Content = function( config){
36562     
36563     this.tpl = config.tpl || false;
36564     
36565     var el = config.el;
36566     var content = config.content;
36567
36568     if(config.autoCreate){ // xtype is available if this is called from factory
36569         el = Roo.id();
36570     }
36571     this.el = Roo.get(el);
36572     if(!this.el && config && config.autoCreate){
36573         if(typeof config.autoCreate == "object"){
36574             if(!config.autoCreate.id){
36575                 config.autoCreate.id = config.id||el;
36576             }
36577             this.el = Roo.DomHelper.append(document.body,
36578                         config.autoCreate, true);
36579         }else{
36580             var elcfg =  {   tag: "div",
36581                             cls: "roo-layout-inactive-content",
36582                             id: config.id||el
36583                             };
36584             if (config.html) {
36585                 elcfg.html = config.html;
36586                 
36587             }
36588                         
36589             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36590         }
36591     } 
36592     this.closable = false;
36593     this.loaded = false;
36594     this.active = false;
36595    
36596       
36597     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36598         
36599         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36600         
36601         this.wrapEl = this.el; //this.el.wrap();
36602         var ti = [];
36603         if (config.toolbar.items) {
36604             ti = config.toolbar.items ;
36605             delete config.toolbar.items ;
36606         }
36607         
36608         var nitems = [];
36609         this.toolbar.render(this.wrapEl, 'before');
36610         for(var i =0;i < ti.length;i++) {
36611           //  Roo.log(['add child', items[i]]);
36612             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36613         }
36614         this.toolbar.items = nitems;
36615         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36616         delete config.toolbar;
36617         
36618     }
36619     /*
36620     // xtype created footer. - not sure if will work as we normally have to render first..
36621     if (this.footer && !this.footer.el && this.footer.xtype) {
36622         if (!this.wrapEl) {
36623             this.wrapEl = this.el.wrap();
36624         }
36625     
36626         this.footer.container = this.wrapEl.createChild();
36627          
36628         this.footer = Roo.factory(this.footer, Roo);
36629         
36630     }
36631     */
36632     
36633      if(typeof config == "string"){
36634         this.title = config;
36635     }else{
36636         Roo.apply(this, config);
36637     }
36638     
36639     if(this.resizeEl){
36640         this.resizeEl = Roo.get(this.resizeEl, true);
36641     }else{
36642         this.resizeEl = this.el;
36643     }
36644     // handle view.xtype
36645     
36646  
36647     
36648     
36649     this.addEvents({
36650         /**
36651          * @event activate
36652          * Fires when this panel is activated. 
36653          * @param {Roo.ContentPanel} this
36654          */
36655         "activate" : true,
36656         /**
36657          * @event deactivate
36658          * Fires when this panel is activated. 
36659          * @param {Roo.ContentPanel} this
36660          */
36661         "deactivate" : true,
36662
36663         /**
36664          * @event resize
36665          * Fires when this panel is resized if fitToFrame is true.
36666          * @param {Roo.ContentPanel} this
36667          * @param {Number} width The width after any component adjustments
36668          * @param {Number} height The height after any component adjustments
36669          */
36670         "resize" : true,
36671         
36672          /**
36673          * @event render
36674          * Fires when this tab is created
36675          * @param {Roo.ContentPanel} this
36676          */
36677         "render" : true
36678         
36679         
36680         
36681     });
36682     
36683
36684     
36685     
36686     if(this.autoScroll){
36687         this.resizeEl.setStyle("overflow", "auto");
36688     } else {
36689         // fix randome scrolling
36690         //this.el.on('scroll', function() {
36691         //    Roo.log('fix random scolling');
36692         //    this.scrollTo('top',0); 
36693         //});
36694     }
36695     content = content || this.content;
36696     if(content){
36697         this.setContent(content);
36698     }
36699     if(config && config.url){
36700         this.setUrl(this.url, this.params, this.loadOnce);
36701     }
36702     
36703     
36704     
36705     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36706     
36707     if (this.view && typeof(this.view.xtype) != 'undefined') {
36708         this.view.el = this.el.appendChild(document.createElement("div"));
36709         this.view = Roo.factory(this.view); 
36710         this.view.render  &&  this.view.render(false, '');  
36711     }
36712     
36713     
36714     this.fireEvent('render', this);
36715 };
36716
36717 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36718     
36719     tabTip : '',
36720     
36721     setRegion : function(region){
36722         this.region = region;
36723         this.setActiveClass(region && !this.background);
36724     },
36725     
36726     
36727     setActiveClass: function(state)
36728     {
36729         if(state){
36730            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36731            this.el.setStyle('position','relative');
36732         }else{
36733            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36734            this.el.setStyle('position', 'absolute');
36735         } 
36736     },
36737     
36738     /**
36739      * Returns the toolbar for this Panel if one was configured. 
36740      * @return {Roo.Toolbar} 
36741      */
36742     getToolbar : function(){
36743         return this.toolbar;
36744     },
36745     
36746     setActiveState : function(active)
36747     {
36748         this.active = active;
36749         this.setActiveClass(active);
36750         if(!active){
36751             if(this.fireEvent("deactivate", this) === false){
36752                 return false;
36753             }
36754             return true;
36755         }
36756         this.fireEvent("activate", this);
36757         return true;
36758     },
36759     /**
36760      * Updates this panel's element
36761      * @param {String} content The new content
36762      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36763     */
36764     setContent : function(content, loadScripts){
36765         this.el.update(content, loadScripts);
36766     },
36767
36768     ignoreResize : function(w, h){
36769         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36770             return true;
36771         }else{
36772             this.lastSize = {width: w, height: h};
36773             return false;
36774         }
36775     },
36776     /**
36777      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36778      * @return {Roo.UpdateManager} The UpdateManager
36779      */
36780     getUpdateManager : function(){
36781         return this.el.getUpdateManager();
36782     },
36783      /**
36784      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36785      * @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:
36786 <pre><code>
36787 panel.load({
36788     url: "your-url.php",
36789     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36790     callback: yourFunction,
36791     scope: yourObject, //(optional scope)
36792     discardUrl: false,
36793     nocache: false,
36794     text: "Loading...",
36795     timeout: 30,
36796     scripts: false
36797 });
36798 </code></pre>
36799      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36800      * 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.
36801      * @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}
36802      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36803      * @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.
36804      * @return {Roo.ContentPanel} this
36805      */
36806     load : function(){
36807         var um = this.el.getUpdateManager();
36808         um.update.apply(um, arguments);
36809         return this;
36810     },
36811
36812
36813     /**
36814      * 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.
36815      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36816      * @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)
36817      * @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)
36818      * @return {Roo.UpdateManager} The UpdateManager
36819      */
36820     setUrl : function(url, params, loadOnce){
36821         if(this.refreshDelegate){
36822             this.removeListener("activate", this.refreshDelegate);
36823         }
36824         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36825         this.on("activate", this.refreshDelegate);
36826         return this.el.getUpdateManager();
36827     },
36828     
36829     _handleRefresh : function(url, params, loadOnce){
36830         if(!loadOnce || !this.loaded){
36831             var updater = this.el.getUpdateManager();
36832             updater.update(url, params, this._setLoaded.createDelegate(this));
36833         }
36834     },
36835     
36836     _setLoaded : function(){
36837         this.loaded = true;
36838     }, 
36839     
36840     /**
36841      * Returns this panel's id
36842      * @return {String} 
36843      */
36844     getId : function(){
36845         return this.el.id;
36846     },
36847     
36848     /** 
36849      * Returns this panel's element - used by regiosn to add.
36850      * @return {Roo.Element} 
36851      */
36852     getEl : function(){
36853         return this.wrapEl || this.el;
36854     },
36855     
36856    
36857     
36858     adjustForComponents : function(width, height)
36859     {
36860         //Roo.log('adjustForComponents ');
36861         if(this.resizeEl != this.el){
36862             width -= this.el.getFrameWidth('lr');
36863             height -= this.el.getFrameWidth('tb');
36864         }
36865         if(this.toolbar){
36866             var te = this.toolbar.getEl();
36867             te.setWidth(width);
36868             height -= te.getHeight();
36869         }
36870         if(this.footer){
36871             var te = this.footer.getEl();
36872             te.setWidth(width);
36873             height -= te.getHeight();
36874         }
36875         
36876         
36877         if(this.adjustments){
36878             width += this.adjustments[0];
36879             height += this.adjustments[1];
36880         }
36881         return {"width": width, "height": height};
36882     },
36883     
36884     setSize : function(width, height){
36885         if(this.fitToFrame && !this.ignoreResize(width, height)){
36886             if(this.fitContainer && this.resizeEl != this.el){
36887                 this.el.setSize(width, height);
36888             }
36889             var size = this.adjustForComponents(width, height);
36890             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36891             this.fireEvent('resize', this, size.width, size.height);
36892         }
36893     },
36894     
36895     /**
36896      * Returns this panel's title
36897      * @return {String} 
36898      */
36899     getTitle : function(){
36900         
36901         if (typeof(this.title) != 'object') {
36902             return this.title;
36903         }
36904         
36905         var t = '';
36906         for (var k in this.title) {
36907             if (!this.title.hasOwnProperty(k)) {
36908                 continue;
36909             }
36910             
36911             if (k.indexOf('-') >= 0) {
36912                 var s = k.split('-');
36913                 for (var i = 0; i<s.length; i++) {
36914                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36915                 }
36916             } else {
36917                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36918             }
36919         }
36920         return t;
36921     },
36922     
36923     /**
36924      * Set this panel's title
36925      * @param {String} title
36926      */
36927     setTitle : function(title){
36928         this.title = title;
36929         if(this.region){
36930             this.region.updatePanelTitle(this, title);
36931         }
36932     },
36933     
36934     /**
36935      * Returns true is this panel was configured to be closable
36936      * @return {Boolean} 
36937      */
36938     isClosable : function(){
36939         return this.closable;
36940     },
36941     
36942     beforeSlide : function(){
36943         this.el.clip();
36944         this.resizeEl.clip();
36945     },
36946     
36947     afterSlide : function(){
36948         this.el.unclip();
36949         this.resizeEl.unclip();
36950     },
36951     
36952     /**
36953      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36954      *   Will fail silently if the {@link #setUrl} method has not been called.
36955      *   This does not activate the panel, just updates its content.
36956      */
36957     refresh : function(){
36958         if(this.refreshDelegate){
36959            this.loaded = false;
36960            this.refreshDelegate();
36961         }
36962     },
36963     
36964     /**
36965      * Destroys this panel
36966      */
36967     destroy : function(){
36968         this.el.removeAllListeners();
36969         var tempEl = document.createElement("span");
36970         tempEl.appendChild(this.el.dom);
36971         tempEl.innerHTML = "";
36972         this.el.remove();
36973         this.el = null;
36974     },
36975     
36976     /**
36977      * form - if the content panel contains a form - this is a reference to it.
36978      * @type {Roo.form.Form}
36979      */
36980     form : false,
36981     /**
36982      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36983      *    This contains a reference to it.
36984      * @type {Roo.View}
36985      */
36986     view : false,
36987     
36988       /**
36989      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36990      * <pre><code>
36991
36992 layout.addxtype({
36993        xtype : 'Form',
36994        items: [ .... ]
36995    }
36996 );
36997
36998 </code></pre>
36999      * @param {Object} cfg Xtype definition of item to add.
37000      */
37001     
37002     
37003     getChildContainer: function () {
37004         return this.getEl();
37005     }
37006     
37007     
37008     /*
37009         var  ret = new Roo.factory(cfg);
37010         return ret;
37011         
37012         
37013         // add form..
37014         if (cfg.xtype.match(/^Form$/)) {
37015             
37016             var el;
37017             //if (this.footer) {
37018             //    el = this.footer.container.insertSibling(false, 'before');
37019             //} else {
37020                 el = this.el.createChild();
37021             //}
37022
37023             this.form = new  Roo.form.Form(cfg);
37024             
37025             
37026             if ( this.form.allItems.length) {
37027                 this.form.render(el.dom);
37028             }
37029             return this.form;
37030         }
37031         // should only have one of theses..
37032         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37033             // views.. should not be just added - used named prop 'view''
37034             
37035             cfg.el = this.el.appendChild(document.createElement("div"));
37036             // factory?
37037             
37038             var ret = new Roo.factory(cfg);
37039              
37040              ret.render && ret.render(false, ''); // render blank..
37041             this.view = ret;
37042             return ret;
37043         }
37044         return false;
37045     }
37046     \*/
37047 });
37048  
37049 /**
37050  * @class Roo.bootstrap.panel.Grid
37051  * @extends Roo.bootstrap.panel.Content
37052  * @constructor
37053  * Create a new GridPanel.
37054  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37055  * @param {Object} config A the config object
37056   
37057  */
37058
37059
37060
37061 Roo.bootstrap.panel.Grid = function(config)
37062 {
37063     
37064       
37065     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37066         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37067
37068     config.el = this.wrapper;
37069     //this.el = this.wrapper;
37070     
37071       if (config.container) {
37072         // ctor'ed from a Border/panel.grid
37073         
37074         
37075         this.wrapper.setStyle("overflow", "hidden");
37076         this.wrapper.addClass('roo-grid-container');
37077
37078     }
37079     
37080     
37081     if(config.toolbar){
37082         var tool_el = this.wrapper.createChild();    
37083         this.toolbar = Roo.factory(config.toolbar);
37084         var ti = [];
37085         if (config.toolbar.items) {
37086             ti = config.toolbar.items ;
37087             delete config.toolbar.items ;
37088         }
37089         
37090         var nitems = [];
37091         this.toolbar.render(tool_el);
37092         for(var i =0;i < ti.length;i++) {
37093           //  Roo.log(['add child', items[i]]);
37094             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37095         }
37096         this.toolbar.items = nitems;
37097         
37098         delete config.toolbar;
37099     }
37100     
37101     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37102     config.grid.scrollBody = true;;
37103     config.grid.monitorWindowResize = false; // turn off autosizing
37104     config.grid.autoHeight = false;
37105     config.grid.autoWidth = false;
37106     
37107     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37108     
37109     if (config.background) {
37110         // render grid on panel activation (if panel background)
37111         this.on('activate', function(gp) {
37112             if (!gp.grid.rendered) {
37113                 gp.grid.render(this.wrapper);
37114                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37115             }
37116         });
37117             
37118     } else {
37119         this.grid.render(this.wrapper);
37120         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37121
37122     }
37123     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37124     // ??? needed ??? config.el = this.wrapper;
37125     
37126     
37127     
37128   
37129     // xtype created footer. - not sure if will work as we normally have to render first..
37130     if (this.footer && !this.footer.el && this.footer.xtype) {
37131         
37132         var ctr = this.grid.getView().getFooterPanel(true);
37133         this.footer.dataSource = this.grid.dataSource;
37134         this.footer = Roo.factory(this.footer, Roo);
37135         this.footer.render(ctr);
37136         
37137     }
37138     
37139     
37140     
37141     
37142      
37143 };
37144
37145 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37146     getId : function(){
37147         return this.grid.id;
37148     },
37149     
37150     /**
37151      * Returns the grid for this panel
37152      * @return {Roo.bootstrap.Table} 
37153      */
37154     getGrid : function(){
37155         return this.grid;    
37156     },
37157     
37158     setSize : function(width, height){
37159         if(!this.ignoreResize(width, height)){
37160             var grid = this.grid;
37161             var size = this.adjustForComponents(width, height);
37162             var gridel = grid.getGridEl();
37163             gridel.setSize(size.width, size.height);
37164             /*
37165             var thd = grid.getGridEl().select('thead',true).first();
37166             var tbd = grid.getGridEl().select('tbody', true).first();
37167             if (tbd) {
37168                 tbd.setSize(width, height - thd.getHeight());
37169             }
37170             */
37171             grid.autoSize();
37172         }
37173     },
37174      
37175     
37176     
37177     beforeSlide : function(){
37178         this.grid.getView().scroller.clip();
37179     },
37180     
37181     afterSlide : function(){
37182         this.grid.getView().scroller.unclip();
37183     },
37184     
37185     destroy : function(){
37186         this.grid.destroy();
37187         delete this.grid;
37188         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37189     }
37190 });
37191
37192 /**
37193  * @class Roo.bootstrap.panel.Nest
37194  * @extends Roo.bootstrap.panel.Content
37195  * @constructor
37196  * Create a new Panel, that can contain a layout.Border.
37197  * 
37198  * 
37199  * @param {Roo.BorderLayout} layout The layout for this panel
37200  * @param {String/Object} config A string to set only the title or a config object
37201  */
37202 Roo.bootstrap.panel.Nest = function(config)
37203 {
37204     // construct with only one argument..
37205     /* FIXME - implement nicer consturctors
37206     if (layout.layout) {
37207         config = layout;
37208         layout = config.layout;
37209         delete config.layout;
37210     }
37211     if (layout.xtype && !layout.getEl) {
37212         // then layout needs constructing..
37213         layout = Roo.factory(layout, Roo);
37214     }
37215     */
37216     
37217     config.el =  config.layout.getEl();
37218     
37219     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37220     
37221     config.layout.monitorWindowResize = false; // turn off autosizing
37222     this.layout = config.layout;
37223     this.layout.getEl().addClass("roo-layout-nested-layout");
37224     
37225     
37226     
37227     
37228 };
37229
37230 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37231
37232     setSize : function(width, height){
37233         if(!this.ignoreResize(width, height)){
37234             var size = this.adjustForComponents(width, height);
37235             var el = this.layout.getEl();
37236             if (size.height < 1) {
37237                 el.setWidth(size.width);   
37238             } else {
37239                 el.setSize(size.width, size.height);
37240             }
37241             var touch = el.dom.offsetWidth;
37242             this.layout.layout();
37243             // ie requires a double layout on the first pass
37244             if(Roo.isIE && !this.initialized){
37245                 this.initialized = true;
37246                 this.layout.layout();
37247             }
37248         }
37249     },
37250     
37251     // activate all subpanels if not currently active..
37252     
37253     setActiveState : function(active){
37254         this.active = active;
37255         this.setActiveClass(active);
37256         
37257         if(!active){
37258             this.fireEvent("deactivate", this);
37259             return;
37260         }
37261         
37262         this.fireEvent("activate", this);
37263         // not sure if this should happen before or after..
37264         if (!this.layout) {
37265             return; // should not happen..
37266         }
37267         var reg = false;
37268         for (var r in this.layout.regions) {
37269             reg = this.layout.getRegion(r);
37270             if (reg.getActivePanel()) {
37271                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37272                 reg.setActivePanel(reg.getActivePanel());
37273                 continue;
37274             }
37275             if (!reg.panels.length) {
37276                 continue;
37277             }
37278             reg.showPanel(reg.getPanel(0));
37279         }
37280         
37281         
37282         
37283         
37284     },
37285     
37286     /**
37287      * Returns the nested BorderLayout for this panel
37288      * @return {Roo.BorderLayout} 
37289      */
37290     getLayout : function(){
37291         return this.layout;
37292     },
37293     
37294      /**
37295      * Adds a xtype elements to the layout of the nested panel
37296      * <pre><code>
37297
37298 panel.addxtype({
37299        xtype : 'ContentPanel',
37300        region: 'west',
37301        items: [ .... ]
37302    }
37303 );
37304
37305 panel.addxtype({
37306         xtype : 'NestedLayoutPanel',
37307         region: 'west',
37308         layout: {
37309            center: { },
37310            west: { }   
37311         },
37312         items : [ ... list of content panels or nested layout panels.. ]
37313    }
37314 );
37315 </code></pre>
37316      * @param {Object} cfg Xtype definition of item to add.
37317      */
37318     addxtype : function(cfg) {
37319         return this.layout.addxtype(cfg);
37320     
37321     }
37322 });        /*
37323  * Based on:
37324  * Ext JS Library 1.1.1
37325  * Copyright(c) 2006-2007, Ext JS, LLC.
37326  *
37327  * Originally Released Under LGPL - original licence link has changed is not relivant.
37328  *
37329  * Fork - LGPL
37330  * <script type="text/javascript">
37331  */
37332 /**
37333  * @class Roo.TabPanel
37334  * @extends Roo.util.Observable
37335  * A lightweight tab container.
37336  * <br><br>
37337  * Usage:
37338  * <pre><code>
37339 // basic tabs 1, built from existing content
37340 var tabs = new Roo.TabPanel("tabs1");
37341 tabs.addTab("script", "View Script");
37342 tabs.addTab("markup", "View Markup");
37343 tabs.activate("script");
37344
37345 // more advanced tabs, built from javascript
37346 var jtabs = new Roo.TabPanel("jtabs");
37347 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37348
37349 // set up the UpdateManager
37350 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37351 var updater = tab2.getUpdateManager();
37352 updater.setDefaultUrl("ajax1.htm");
37353 tab2.on('activate', updater.refresh, updater, true);
37354
37355 // Use setUrl for Ajax loading
37356 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37357 tab3.setUrl("ajax2.htm", null, true);
37358
37359 // Disabled tab
37360 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37361 tab4.disable();
37362
37363 jtabs.activate("jtabs-1");
37364  * </code></pre>
37365  * @constructor
37366  * Create a new TabPanel.
37367  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37368  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37369  */
37370 Roo.bootstrap.panel.Tabs = function(config){
37371     /**
37372     * The container element for this TabPanel.
37373     * @type Roo.Element
37374     */
37375     this.el = Roo.get(config.el);
37376     delete config.el;
37377     if(config){
37378         if(typeof config == "boolean"){
37379             this.tabPosition = config ? "bottom" : "top";
37380         }else{
37381             Roo.apply(this, config);
37382         }
37383     }
37384     
37385     if(this.tabPosition == "bottom"){
37386         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37387         this.el.addClass("roo-tabs-bottom");
37388     }
37389     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37390     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37391     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37392     if(Roo.isIE){
37393         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37394     }
37395     if(this.tabPosition != "bottom"){
37396         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37397          * @type Roo.Element
37398          */
37399         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37400         this.el.addClass("roo-tabs-top");
37401     }
37402     this.items = [];
37403
37404     this.bodyEl.setStyle("position", "relative");
37405
37406     this.active = null;
37407     this.activateDelegate = this.activate.createDelegate(this);
37408
37409     this.addEvents({
37410         /**
37411          * @event tabchange
37412          * Fires when the active tab changes
37413          * @param {Roo.TabPanel} this
37414          * @param {Roo.TabPanelItem} activePanel The new active tab
37415          */
37416         "tabchange": true,
37417         /**
37418          * @event beforetabchange
37419          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37420          * @param {Roo.TabPanel} this
37421          * @param {Object} e Set cancel to true on this object to cancel the tab change
37422          * @param {Roo.TabPanelItem} tab The tab being changed to
37423          */
37424         "beforetabchange" : true
37425     });
37426
37427     Roo.EventManager.onWindowResize(this.onResize, this);
37428     this.cpad = this.el.getPadding("lr");
37429     this.hiddenCount = 0;
37430
37431
37432     // toolbar on the tabbar support...
37433     if (this.toolbar) {
37434         alert("no toolbar support yet");
37435         this.toolbar  = false;
37436         /*
37437         var tcfg = this.toolbar;
37438         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37439         this.toolbar = new Roo.Toolbar(tcfg);
37440         if (Roo.isSafari) {
37441             var tbl = tcfg.container.child('table', true);
37442             tbl.setAttribute('width', '100%');
37443         }
37444         */
37445         
37446     }
37447    
37448
37449
37450     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37451 };
37452
37453 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37454     /*
37455      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37456      */
37457     tabPosition : "top",
37458     /*
37459      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37460      */
37461     currentTabWidth : 0,
37462     /*
37463      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37464      */
37465     minTabWidth : 40,
37466     /*
37467      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37468      */
37469     maxTabWidth : 250,
37470     /*
37471      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37472      */
37473     preferredTabWidth : 175,
37474     /*
37475      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37476      */
37477     resizeTabs : false,
37478     /*
37479      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37480      */
37481     monitorResize : true,
37482     /*
37483      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37484      */
37485     toolbar : false,
37486
37487     /**
37488      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37489      * @param {String} id The id of the div to use <b>or create</b>
37490      * @param {String} text The text for the tab
37491      * @param {String} content (optional) Content to put in the TabPanelItem body
37492      * @param {Boolean} closable (optional) True to create a close icon on the tab
37493      * @return {Roo.TabPanelItem} The created TabPanelItem
37494      */
37495     addTab : function(id, text, content, closable, tpl)
37496     {
37497         var item = new Roo.bootstrap.panel.TabItem({
37498             panel: this,
37499             id : id,
37500             text : text,
37501             closable : closable,
37502             tpl : tpl
37503         });
37504         this.addTabItem(item);
37505         if(content){
37506             item.setContent(content);
37507         }
37508         return item;
37509     },
37510
37511     /**
37512      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37513      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37514      * @return {Roo.TabPanelItem}
37515      */
37516     getTab : function(id){
37517         return this.items[id];
37518     },
37519
37520     /**
37521      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37522      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37523      */
37524     hideTab : function(id){
37525         var t = this.items[id];
37526         if(!t.isHidden()){
37527            t.setHidden(true);
37528            this.hiddenCount++;
37529            this.autoSizeTabs();
37530         }
37531     },
37532
37533     /**
37534      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37535      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37536      */
37537     unhideTab : function(id){
37538         var t = this.items[id];
37539         if(t.isHidden()){
37540            t.setHidden(false);
37541            this.hiddenCount--;
37542            this.autoSizeTabs();
37543         }
37544     },
37545
37546     /**
37547      * Adds an existing {@link Roo.TabPanelItem}.
37548      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37549      */
37550     addTabItem : function(item){
37551         this.items[item.id] = item;
37552         this.items.push(item);
37553       //  if(this.resizeTabs){
37554     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37555   //         this.autoSizeTabs();
37556 //        }else{
37557 //            item.autoSize();
37558        // }
37559     },
37560
37561     /**
37562      * Removes a {@link Roo.TabPanelItem}.
37563      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37564      */
37565     removeTab : function(id){
37566         var items = this.items;
37567         var tab = items[id];
37568         if(!tab) { return; }
37569         var index = items.indexOf(tab);
37570         if(this.active == tab && items.length > 1){
37571             var newTab = this.getNextAvailable(index);
37572             if(newTab) {
37573                 newTab.activate();
37574             }
37575         }
37576         this.stripEl.dom.removeChild(tab.pnode.dom);
37577         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37578             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37579         }
37580         items.splice(index, 1);
37581         delete this.items[tab.id];
37582         tab.fireEvent("close", tab);
37583         tab.purgeListeners();
37584         this.autoSizeTabs();
37585     },
37586
37587     getNextAvailable : function(start){
37588         var items = this.items;
37589         var index = start;
37590         // look for a next tab that will slide over to
37591         // replace the one being removed
37592         while(index < items.length){
37593             var item = items[++index];
37594             if(item && !item.isHidden()){
37595                 return item;
37596             }
37597         }
37598         // if one isn't found select the previous tab (on the left)
37599         index = start;
37600         while(index >= 0){
37601             var item = items[--index];
37602             if(item && !item.isHidden()){
37603                 return item;
37604             }
37605         }
37606         return null;
37607     },
37608
37609     /**
37610      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37611      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37612      */
37613     disableTab : function(id){
37614         var tab = this.items[id];
37615         if(tab && this.active != tab){
37616             tab.disable();
37617         }
37618     },
37619
37620     /**
37621      * Enables a {@link Roo.TabPanelItem} that is disabled.
37622      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37623      */
37624     enableTab : function(id){
37625         var tab = this.items[id];
37626         tab.enable();
37627     },
37628
37629     /**
37630      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37631      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37632      * @return {Roo.TabPanelItem} The TabPanelItem.
37633      */
37634     activate : function(id){
37635         var tab = this.items[id];
37636         if(!tab){
37637             return null;
37638         }
37639         if(tab == this.active || tab.disabled){
37640             return tab;
37641         }
37642         var e = {};
37643         this.fireEvent("beforetabchange", this, e, tab);
37644         if(e.cancel !== true && !tab.disabled){
37645             if(this.active){
37646                 this.active.hide();
37647             }
37648             this.active = this.items[id];
37649             this.active.show();
37650             this.fireEvent("tabchange", this, this.active);
37651         }
37652         return tab;
37653     },
37654
37655     /**
37656      * Gets the active {@link Roo.TabPanelItem}.
37657      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37658      */
37659     getActiveTab : function(){
37660         return this.active;
37661     },
37662
37663     /**
37664      * Updates the tab body element to fit the height of the container element
37665      * for overflow scrolling
37666      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37667      */
37668     syncHeight : function(targetHeight){
37669         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37670         var bm = this.bodyEl.getMargins();
37671         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37672         this.bodyEl.setHeight(newHeight);
37673         return newHeight;
37674     },
37675
37676     onResize : function(){
37677         if(this.monitorResize){
37678             this.autoSizeTabs();
37679         }
37680     },
37681
37682     /**
37683      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37684      */
37685     beginUpdate : function(){
37686         this.updating = true;
37687     },
37688
37689     /**
37690      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37691      */
37692     endUpdate : function(){
37693         this.updating = false;
37694         this.autoSizeTabs();
37695     },
37696
37697     /**
37698      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37699      */
37700     autoSizeTabs : function(){
37701         var count = this.items.length;
37702         var vcount = count - this.hiddenCount;
37703         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37704             return;
37705         }
37706         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37707         var availWidth = Math.floor(w / vcount);
37708         var b = this.stripBody;
37709         if(b.getWidth() > w){
37710             var tabs = this.items;
37711             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37712             if(availWidth < this.minTabWidth){
37713                 /*if(!this.sleft){    // incomplete scrolling code
37714                     this.createScrollButtons();
37715                 }
37716                 this.showScroll();
37717                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37718             }
37719         }else{
37720             if(this.currentTabWidth < this.preferredTabWidth){
37721                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37722             }
37723         }
37724     },
37725
37726     /**
37727      * Returns the number of tabs in this TabPanel.
37728      * @return {Number}
37729      */
37730      getCount : function(){
37731          return this.items.length;
37732      },
37733
37734     /**
37735      * Resizes all the tabs to the passed width
37736      * @param {Number} The new width
37737      */
37738     setTabWidth : function(width){
37739         this.currentTabWidth = width;
37740         for(var i = 0, len = this.items.length; i < len; i++) {
37741                 if(!this.items[i].isHidden()) {
37742                 this.items[i].setWidth(width);
37743             }
37744         }
37745     },
37746
37747     /**
37748      * Destroys this TabPanel
37749      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37750      */
37751     destroy : function(removeEl){
37752         Roo.EventManager.removeResizeListener(this.onResize, this);
37753         for(var i = 0, len = this.items.length; i < len; i++){
37754             this.items[i].purgeListeners();
37755         }
37756         if(removeEl === true){
37757             this.el.update("");
37758             this.el.remove();
37759         }
37760     },
37761     
37762     createStrip : function(container)
37763     {
37764         var strip = document.createElement("nav");
37765         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37766         container.appendChild(strip);
37767         return strip;
37768     },
37769     
37770     createStripList : function(strip)
37771     {
37772         // div wrapper for retard IE
37773         // returns the "tr" element.
37774         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37775         //'<div class="x-tabs-strip-wrap">'+
37776           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37777           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37778         return strip.firstChild; //.firstChild.firstChild.firstChild;
37779     },
37780     createBody : function(container)
37781     {
37782         var body = document.createElement("div");
37783         Roo.id(body, "tab-body");
37784         //Roo.fly(body).addClass("x-tabs-body");
37785         Roo.fly(body).addClass("tab-content");
37786         container.appendChild(body);
37787         return body;
37788     },
37789     createItemBody :function(bodyEl, id){
37790         var body = Roo.getDom(id);
37791         if(!body){
37792             body = document.createElement("div");
37793             body.id = id;
37794         }
37795         //Roo.fly(body).addClass("x-tabs-item-body");
37796         Roo.fly(body).addClass("tab-pane");
37797          bodyEl.insertBefore(body, bodyEl.firstChild);
37798         return body;
37799     },
37800     /** @private */
37801     createStripElements :  function(stripEl, text, closable, tpl)
37802     {
37803         var td = document.createElement("li"); // was td..
37804         
37805         
37806         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37807         
37808         
37809         stripEl.appendChild(td);
37810         /*if(closable){
37811             td.className = "x-tabs-closable";
37812             if(!this.closeTpl){
37813                 this.closeTpl = new Roo.Template(
37814                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37815                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37816                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37817                 );
37818             }
37819             var el = this.closeTpl.overwrite(td, {"text": text});
37820             var close = el.getElementsByTagName("div")[0];
37821             var inner = el.getElementsByTagName("em")[0];
37822             return {"el": el, "close": close, "inner": inner};
37823         } else {
37824         */
37825         // not sure what this is..
37826 //            if(!this.tabTpl){
37827                 //this.tabTpl = new Roo.Template(
37828                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37829                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37830                 //);
37831 //                this.tabTpl = new Roo.Template(
37832 //                   '<a href="#">' +
37833 //                   '<span unselectable="on"' +
37834 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37835 //                            ' >{text}</span></a>'
37836 //                );
37837 //                
37838 //            }
37839
37840
37841             var template = tpl || this.tabTpl || false;
37842             
37843             if(!template){
37844                 
37845                 template = new Roo.Template(
37846                    '<a href="#">' +
37847                    '<span unselectable="on"' +
37848                             (this.disableTooltips ? '' : ' title="{text}"') +
37849                             ' >{text}</span></a>'
37850                 );
37851             }
37852             
37853             switch (typeof(template)) {
37854                 case 'object' :
37855                     break;
37856                 case 'string' :
37857                     template = new Roo.Template(template);
37858                     break;
37859                 default :
37860                     break;
37861             }
37862             
37863             var el = template.overwrite(td, {"text": text});
37864             
37865             var inner = el.getElementsByTagName("span")[0];
37866             
37867             return {"el": el, "inner": inner};
37868             
37869     }
37870         
37871     
37872 });
37873
37874 /**
37875  * @class Roo.TabPanelItem
37876  * @extends Roo.util.Observable
37877  * Represents an individual item (tab plus body) in a TabPanel.
37878  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37879  * @param {String} id The id of this TabPanelItem
37880  * @param {String} text The text for the tab of this TabPanelItem
37881  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37882  */
37883 Roo.bootstrap.panel.TabItem = function(config){
37884     /**
37885      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37886      * @type Roo.TabPanel
37887      */
37888     this.tabPanel = config.panel;
37889     /**
37890      * The id for this TabPanelItem
37891      * @type String
37892      */
37893     this.id = config.id;
37894     /** @private */
37895     this.disabled = false;
37896     /** @private */
37897     this.text = config.text;
37898     /** @private */
37899     this.loaded = false;
37900     this.closable = config.closable;
37901
37902     /**
37903      * The body element for this TabPanelItem.
37904      * @type Roo.Element
37905      */
37906     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37907     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37908     this.bodyEl.setStyle("display", "block");
37909     this.bodyEl.setStyle("zoom", "1");
37910     //this.hideAction();
37911
37912     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37913     /** @private */
37914     this.el = Roo.get(els.el);
37915     this.inner = Roo.get(els.inner, true);
37916     this.textEl = Roo.get(this.el.dom.firstChild, true);
37917     this.pnode = Roo.get(els.el.parentNode, true);
37918 //    this.el.on("mousedown", this.onTabMouseDown, this);
37919     this.el.on("click", this.onTabClick, this);
37920     /** @private */
37921     if(config.closable){
37922         var c = Roo.get(els.close, true);
37923         c.dom.title = this.closeText;
37924         c.addClassOnOver("close-over");
37925         c.on("click", this.closeClick, this);
37926      }
37927
37928     this.addEvents({
37929          /**
37930          * @event activate
37931          * Fires when this tab becomes the active tab.
37932          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37933          * @param {Roo.TabPanelItem} this
37934          */
37935         "activate": true,
37936         /**
37937          * @event beforeclose
37938          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37939          * @param {Roo.TabPanelItem} this
37940          * @param {Object} e Set cancel to true on this object to cancel the close.
37941          */
37942         "beforeclose": true,
37943         /**
37944          * @event close
37945          * Fires when this tab is closed.
37946          * @param {Roo.TabPanelItem} this
37947          */
37948          "close": true,
37949         /**
37950          * @event deactivate
37951          * Fires when this tab is no longer the active tab.
37952          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37953          * @param {Roo.TabPanelItem} this
37954          */
37955          "deactivate" : true
37956     });
37957     this.hidden = false;
37958
37959     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37960 };
37961
37962 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37963            {
37964     purgeListeners : function(){
37965        Roo.util.Observable.prototype.purgeListeners.call(this);
37966        this.el.removeAllListeners();
37967     },
37968     /**
37969      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37970      */
37971     show : function(){
37972         this.pnode.addClass("active");
37973         this.showAction();
37974         if(Roo.isOpera){
37975             this.tabPanel.stripWrap.repaint();
37976         }
37977         this.fireEvent("activate", this.tabPanel, this);
37978     },
37979
37980     /**
37981      * Returns true if this tab is the active tab.
37982      * @return {Boolean}
37983      */
37984     isActive : function(){
37985         return this.tabPanel.getActiveTab() == this;
37986     },
37987
37988     /**
37989      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37990      */
37991     hide : function(){
37992         this.pnode.removeClass("active");
37993         this.hideAction();
37994         this.fireEvent("deactivate", this.tabPanel, this);
37995     },
37996
37997     hideAction : function(){
37998         this.bodyEl.hide();
37999         this.bodyEl.setStyle("position", "absolute");
38000         this.bodyEl.setLeft("-20000px");
38001         this.bodyEl.setTop("-20000px");
38002     },
38003
38004     showAction : function(){
38005         this.bodyEl.setStyle("position", "relative");
38006         this.bodyEl.setTop("");
38007         this.bodyEl.setLeft("");
38008         this.bodyEl.show();
38009     },
38010
38011     /**
38012      * Set the tooltip for the tab.
38013      * @param {String} tooltip The tab's tooltip
38014      */
38015     setTooltip : function(text){
38016         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38017             this.textEl.dom.qtip = text;
38018             this.textEl.dom.removeAttribute('title');
38019         }else{
38020             this.textEl.dom.title = text;
38021         }
38022     },
38023
38024     onTabClick : function(e){
38025         e.preventDefault();
38026         this.tabPanel.activate(this.id);
38027     },
38028
38029     onTabMouseDown : function(e){
38030         e.preventDefault();
38031         this.tabPanel.activate(this.id);
38032     },
38033 /*
38034     getWidth : function(){
38035         return this.inner.getWidth();
38036     },
38037
38038     setWidth : function(width){
38039         var iwidth = width - this.pnode.getPadding("lr");
38040         this.inner.setWidth(iwidth);
38041         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38042         this.pnode.setWidth(width);
38043     },
38044 */
38045     /**
38046      * Show or hide the tab
38047      * @param {Boolean} hidden True to hide or false to show.
38048      */
38049     setHidden : function(hidden){
38050         this.hidden = hidden;
38051         this.pnode.setStyle("display", hidden ? "none" : "");
38052     },
38053
38054     /**
38055      * Returns true if this tab is "hidden"
38056      * @return {Boolean}
38057      */
38058     isHidden : function(){
38059         return this.hidden;
38060     },
38061
38062     /**
38063      * Returns the text for this tab
38064      * @return {String}
38065      */
38066     getText : function(){
38067         return this.text;
38068     },
38069     /*
38070     autoSize : function(){
38071         //this.el.beginMeasure();
38072         this.textEl.setWidth(1);
38073         /*
38074          *  #2804 [new] Tabs in Roojs
38075          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38076          */
38077         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38078         //this.el.endMeasure();
38079     //},
38080
38081     /**
38082      * Sets the text for the tab (Note: this also sets the tooltip text)
38083      * @param {String} text The tab's text and tooltip
38084      */
38085     setText : function(text){
38086         this.text = text;
38087         this.textEl.update(text);
38088         this.setTooltip(text);
38089         //if(!this.tabPanel.resizeTabs){
38090         //    this.autoSize();
38091         //}
38092     },
38093     /**
38094      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38095      */
38096     activate : function(){
38097         this.tabPanel.activate(this.id);
38098     },
38099
38100     /**
38101      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38102      */
38103     disable : function(){
38104         if(this.tabPanel.active != this){
38105             this.disabled = true;
38106             this.pnode.addClass("disabled");
38107         }
38108     },
38109
38110     /**
38111      * Enables this TabPanelItem if it was previously disabled.
38112      */
38113     enable : function(){
38114         this.disabled = false;
38115         this.pnode.removeClass("disabled");
38116     },
38117
38118     /**
38119      * Sets the content for this TabPanelItem.
38120      * @param {String} content The content
38121      * @param {Boolean} loadScripts true to look for and load scripts
38122      */
38123     setContent : function(content, loadScripts){
38124         this.bodyEl.update(content, loadScripts);
38125     },
38126
38127     /**
38128      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38129      * @return {Roo.UpdateManager} The UpdateManager
38130      */
38131     getUpdateManager : function(){
38132         return this.bodyEl.getUpdateManager();
38133     },
38134
38135     /**
38136      * Set a URL to be used to load the content for this TabPanelItem.
38137      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38138      * @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)
38139      * @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)
38140      * @return {Roo.UpdateManager} The UpdateManager
38141      */
38142     setUrl : function(url, params, loadOnce){
38143         if(this.refreshDelegate){
38144             this.un('activate', this.refreshDelegate);
38145         }
38146         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38147         this.on("activate", this.refreshDelegate);
38148         return this.bodyEl.getUpdateManager();
38149     },
38150
38151     /** @private */
38152     _handleRefresh : function(url, params, loadOnce){
38153         if(!loadOnce || !this.loaded){
38154             var updater = this.bodyEl.getUpdateManager();
38155             updater.update(url, params, this._setLoaded.createDelegate(this));
38156         }
38157     },
38158
38159     /**
38160      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38161      *   Will fail silently if the setUrl method has not been called.
38162      *   This does not activate the panel, just updates its content.
38163      */
38164     refresh : function(){
38165         if(this.refreshDelegate){
38166            this.loaded = false;
38167            this.refreshDelegate();
38168         }
38169     },
38170
38171     /** @private */
38172     _setLoaded : function(){
38173         this.loaded = true;
38174     },
38175
38176     /** @private */
38177     closeClick : function(e){
38178         var o = {};
38179         e.stopEvent();
38180         this.fireEvent("beforeclose", this, o);
38181         if(o.cancel !== true){
38182             this.tabPanel.removeTab(this.id);
38183         }
38184     },
38185     /**
38186      * The text displayed in the tooltip for the close icon.
38187      * @type String
38188      */
38189     closeText : "Close this tab"
38190 });
38191 /**
38192 *    This script refer to:
38193 *    Title: International Telephone Input
38194 *    Author: Jack O'Connor
38195 *    Code version:  v12.1.12
38196 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38197 **/
38198
38199 Roo.bootstrap.PhoneInputData = function() {
38200     var d = [
38201       [
38202         "Afghanistan (‫افغانستان‬‎)",
38203         "af",
38204         "93"
38205       ],
38206       [
38207         "Albania (Shqipëri)",
38208         "al",
38209         "355"
38210       ],
38211       [
38212         "Algeria (‫الجزائر‬‎)",
38213         "dz",
38214         "213"
38215       ],
38216       [
38217         "American Samoa",
38218         "as",
38219         "1684"
38220       ],
38221       [
38222         "Andorra",
38223         "ad",
38224         "376"
38225       ],
38226       [
38227         "Angola",
38228         "ao",
38229         "244"
38230       ],
38231       [
38232         "Anguilla",
38233         "ai",
38234         "1264"
38235       ],
38236       [
38237         "Antigua and Barbuda",
38238         "ag",
38239         "1268"
38240       ],
38241       [
38242         "Argentina",
38243         "ar",
38244         "54"
38245       ],
38246       [
38247         "Armenia (Հայաստան)",
38248         "am",
38249         "374"
38250       ],
38251       [
38252         "Aruba",
38253         "aw",
38254         "297"
38255       ],
38256       [
38257         "Australia",
38258         "au",
38259         "61",
38260         0
38261       ],
38262       [
38263         "Austria (Österreich)",
38264         "at",
38265         "43"
38266       ],
38267       [
38268         "Azerbaijan (Azərbaycan)",
38269         "az",
38270         "994"
38271       ],
38272       [
38273         "Bahamas",
38274         "bs",
38275         "1242"
38276       ],
38277       [
38278         "Bahrain (‫البحرين‬‎)",
38279         "bh",
38280         "973"
38281       ],
38282       [
38283         "Bangladesh (বাংলাদেশ)",
38284         "bd",
38285         "880"
38286       ],
38287       [
38288         "Barbados",
38289         "bb",
38290         "1246"
38291       ],
38292       [
38293         "Belarus (Беларусь)",
38294         "by",
38295         "375"
38296       ],
38297       [
38298         "Belgium (België)",
38299         "be",
38300         "32"
38301       ],
38302       [
38303         "Belize",
38304         "bz",
38305         "501"
38306       ],
38307       [
38308         "Benin (Bénin)",
38309         "bj",
38310         "229"
38311       ],
38312       [
38313         "Bermuda",
38314         "bm",
38315         "1441"
38316       ],
38317       [
38318         "Bhutan (འབྲུག)",
38319         "bt",
38320         "975"
38321       ],
38322       [
38323         "Bolivia",
38324         "bo",
38325         "591"
38326       ],
38327       [
38328         "Bosnia and Herzegovina (Босна и Херцеговина)",
38329         "ba",
38330         "387"
38331       ],
38332       [
38333         "Botswana",
38334         "bw",
38335         "267"
38336       ],
38337       [
38338         "Brazil (Brasil)",
38339         "br",
38340         "55"
38341       ],
38342       [
38343         "British Indian Ocean Territory",
38344         "io",
38345         "246"
38346       ],
38347       [
38348         "British Virgin Islands",
38349         "vg",
38350         "1284"
38351       ],
38352       [
38353         "Brunei",
38354         "bn",
38355         "673"
38356       ],
38357       [
38358         "Bulgaria (България)",
38359         "bg",
38360         "359"
38361       ],
38362       [
38363         "Burkina Faso",
38364         "bf",
38365         "226"
38366       ],
38367       [
38368         "Burundi (Uburundi)",
38369         "bi",
38370         "257"
38371       ],
38372       [
38373         "Cambodia (កម្ពុជា)",
38374         "kh",
38375         "855"
38376       ],
38377       [
38378         "Cameroon (Cameroun)",
38379         "cm",
38380         "237"
38381       ],
38382       [
38383         "Canada",
38384         "ca",
38385         "1",
38386         1,
38387         ["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"]
38388       ],
38389       [
38390         "Cape Verde (Kabu Verdi)",
38391         "cv",
38392         "238"
38393       ],
38394       [
38395         "Caribbean Netherlands",
38396         "bq",
38397         "599",
38398         1
38399       ],
38400       [
38401         "Cayman Islands",
38402         "ky",
38403         "1345"
38404       ],
38405       [
38406         "Central African Republic (République centrafricaine)",
38407         "cf",
38408         "236"
38409       ],
38410       [
38411         "Chad (Tchad)",
38412         "td",
38413         "235"
38414       ],
38415       [
38416         "Chile",
38417         "cl",
38418         "56"
38419       ],
38420       [
38421         "China (中国)",
38422         "cn",
38423         "86"
38424       ],
38425       [
38426         "Christmas Island",
38427         "cx",
38428         "61",
38429         2
38430       ],
38431       [
38432         "Cocos (Keeling) Islands",
38433         "cc",
38434         "61",
38435         1
38436       ],
38437       [
38438         "Colombia",
38439         "co",
38440         "57"
38441       ],
38442       [
38443         "Comoros (‫جزر القمر‬‎)",
38444         "km",
38445         "269"
38446       ],
38447       [
38448         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38449         "cd",
38450         "243"
38451       ],
38452       [
38453         "Congo (Republic) (Congo-Brazzaville)",
38454         "cg",
38455         "242"
38456       ],
38457       [
38458         "Cook Islands",
38459         "ck",
38460         "682"
38461       ],
38462       [
38463         "Costa Rica",
38464         "cr",
38465         "506"
38466       ],
38467       [
38468         "Côte d’Ivoire",
38469         "ci",
38470         "225"
38471       ],
38472       [
38473         "Croatia (Hrvatska)",
38474         "hr",
38475         "385"
38476       ],
38477       [
38478         "Cuba",
38479         "cu",
38480         "53"
38481       ],
38482       [
38483         "Curaçao",
38484         "cw",
38485         "599",
38486         0
38487       ],
38488       [
38489         "Cyprus (Κύπρος)",
38490         "cy",
38491         "357"
38492       ],
38493       [
38494         "Czech Republic (Česká republika)",
38495         "cz",
38496         "420"
38497       ],
38498       [
38499         "Denmark (Danmark)",
38500         "dk",
38501         "45"
38502       ],
38503       [
38504         "Djibouti",
38505         "dj",
38506         "253"
38507       ],
38508       [
38509         "Dominica",
38510         "dm",
38511         "1767"
38512       ],
38513       [
38514         "Dominican Republic (República Dominicana)",
38515         "do",
38516         "1",
38517         2,
38518         ["809", "829", "849"]
38519       ],
38520       [
38521         "Ecuador",
38522         "ec",
38523         "593"
38524       ],
38525       [
38526         "Egypt (‫مصر‬‎)",
38527         "eg",
38528         "20"
38529       ],
38530       [
38531         "El Salvador",
38532         "sv",
38533         "503"
38534       ],
38535       [
38536         "Equatorial Guinea (Guinea Ecuatorial)",
38537         "gq",
38538         "240"
38539       ],
38540       [
38541         "Eritrea",
38542         "er",
38543         "291"
38544       ],
38545       [
38546         "Estonia (Eesti)",
38547         "ee",
38548         "372"
38549       ],
38550       [
38551         "Ethiopia",
38552         "et",
38553         "251"
38554       ],
38555       [
38556         "Falkland Islands (Islas Malvinas)",
38557         "fk",
38558         "500"
38559       ],
38560       [
38561         "Faroe Islands (Føroyar)",
38562         "fo",
38563         "298"
38564       ],
38565       [
38566         "Fiji",
38567         "fj",
38568         "679"
38569       ],
38570       [
38571         "Finland (Suomi)",
38572         "fi",
38573         "358",
38574         0
38575       ],
38576       [
38577         "France",
38578         "fr",
38579         "33"
38580       ],
38581       [
38582         "French Guiana (Guyane française)",
38583         "gf",
38584         "594"
38585       ],
38586       [
38587         "French Polynesia (Polynésie française)",
38588         "pf",
38589         "689"
38590       ],
38591       [
38592         "Gabon",
38593         "ga",
38594         "241"
38595       ],
38596       [
38597         "Gambia",
38598         "gm",
38599         "220"
38600       ],
38601       [
38602         "Georgia (საქართველო)",
38603         "ge",
38604         "995"
38605       ],
38606       [
38607         "Germany (Deutschland)",
38608         "de",
38609         "49"
38610       ],
38611       [
38612         "Ghana (Gaana)",
38613         "gh",
38614         "233"
38615       ],
38616       [
38617         "Gibraltar",
38618         "gi",
38619         "350"
38620       ],
38621       [
38622         "Greece (Ελλάδα)",
38623         "gr",
38624         "30"
38625       ],
38626       [
38627         "Greenland (Kalaallit Nunaat)",
38628         "gl",
38629         "299"
38630       ],
38631       [
38632         "Grenada",
38633         "gd",
38634         "1473"
38635       ],
38636       [
38637         "Guadeloupe",
38638         "gp",
38639         "590",
38640         0
38641       ],
38642       [
38643         "Guam",
38644         "gu",
38645         "1671"
38646       ],
38647       [
38648         "Guatemala",
38649         "gt",
38650         "502"
38651       ],
38652       [
38653         "Guernsey",
38654         "gg",
38655         "44",
38656         1
38657       ],
38658       [
38659         "Guinea (Guinée)",
38660         "gn",
38661         "224"
38662       ],
38663       [
38664         "Guinea-Bissau (Guiné Bissau)",
38665         "gw",
38666         "245"
38667       ],
38668       [
38669         "Guyana",
38670         "gy",
38671         "592"
38672       ],
38673       [
38674         "Haiti",
38675         "ht",
38676         "509"
38677       ],
38678       [
38679         "Honduras",
38680         "hn",
38681         "504"
38682       ],
38683       [
38684         "Hong Kong (香港)",
38685         "hk",
38686         "852"
38687       ],
38688       [
38689         "Hungary (Magyarország)",
38690         "hu",
38691         "36"
38692       ],
38693       [
38694         "Iceland (Ísland)",
38695         "is",
38696         "354"
38697       ],
38698       [
38699         "India (भारत)",
38700         "in",
38701         "91"
38702       ],
38703       [
38704         "Indonesia",
38705         "id",
38706         "62"
38707       ],
38708       [
38709         "Iran (‫ایران‬‎)",
38710         "ir",
38711         "98"
38712       ],
38713       [
38714         "Iraq (‫العراق‬‎)",
38715         "iq",
38716         "964"
38717       ],
38718       [
38719         "Ireland",
38720         "ie",
38721         "353"
38722       ],
38723       [
38724         "Isle of Man",
38725         "im",
38726         "44",
38727         2
38728       ],
38729       [
38730         "Israel (‫ישראל‬‎)",
38731         "il",
38732         "972"
38733       ],
38734       [
38735         "Italy (Italia)",
38736         "it",
38737         "39",
38738         0
38739       ],
38740       [
38741         "Jamaica",
38742         "jm",
38743         "1876"
38744       ],
38745       [
38746         "Japan (日本)",
38747         "jp",
38748         "81"
38749       ],
38750       [
38751         "Jersey",
38752         "je",
38753         "44",
38754         3
38755       ],
38756       [
38757         "Jordan (‫الأردن‬‎)",
38758         "jo",
38759         "962"
38760       ],
38761       [
38762         "Kazakhstan (Казахстан)",
38763         "kz",
38764         "7",
38765         1
38766       ],
38767       [
38768         "Kenya",
38769         "ke",
38770         "254"
38771       ],
38772       [
38773         "Kiribati",
38774         "ki",
38775         "686"
38776       ],
38777       [
38778         "Kosovo",
38779         "xk",
38780         "383"
38781       ],
38782       [
38783         "Kuwait (‫الكويت‬‎)",
38784         "kw",
38785         "965"
38786       ],
38787       [
38788         "Kyrgyzstan (Кыргызстан)",
38789         "kg",
38790         "996"
38791       ],
38792       [
38793         "Laos (ລາວ)",
38794         "la",
38795         "856"
38796       ],
38797       [
38798         "Latvia (Latvija)",
38799         "lv",
38800         "371"
38801       ],
38802       [
38803         "Lebanon (‫لبنان‬‎)",
38804         "lb",
38805         "961"
38806       ],
38807       [
38808         "Lesotho",
38809         "ls",
38810         "266"
38811       ],
38812       [
38813         "Liberia",
38814         "lr",
38815         "231"
38816       ],
38817       [
38818         "Libya (‫ليبيا‬‎)",
38819         "ly",
38820         "218"
38821       ],
38822       [
38823         "Liechtenstein",
38824         "li",
38825         "423"
38826       ],
38827       [
38828         "Lithuania (Lietuva)",
38829         "lt",
38830         "370"
38831       ],
38832       [
38833         "Luxembourg",
38834         "lu",
38835         "352"
38836       ],
38837       [
38838         "Macau (澳門)",
38839         "mo",
38840         "853"
38841       ],
38842       [
38843         "Macedonia (FYROM) (Македонија)",
38844         "mk",
38845         "389"
38846       ],
38847       [
38848         "Madagascar (Madagasikara)",
38849         "mg",
38850         "261"
38851       ],
38852       [
38853         "Malawi",
38854         "mw",
38855         "265"
38856       ],
38857       [
38858         "Malaysia",
38859         "my",
38860         "60"
38861       ],
38862       [
38863         "Maldives",
38864         "mv",
38865         "960"
38866       ],
38867       [
38868         "Mali",
38869         "ml",
38870         "223"
38871       ],
38872       [
38873         "Malta",
38874         "mt",
38875         "356"
38876       ],
38877       [
38878         "Marshall Islands",
38879         "mh",
38880         "692"
38881       ],
38882       [
38883         "Martinique",
38884         "mq",
38885         "596"
38886       ],
38887       [
38888         "Mauritania (‫موريتانيا‬‎)",
38889         "mr",
38890         "222"
38891       ],
38892       [
38893         "Mauritius (Moris)",
38894         "mu",
38895         "230"
38896       ],
38897       [
38898         "Mayotte",
38899         "yt",
38900         "262",
38901         1
38902       ],
38903       [
38904         "Mexico (México)",
38905         "mx",
38906         "52"
38907       ],
38908       [
38909         "Micronesia",
38910         "fm",
38911         "691"
38912       ],
38913       [
38914         "Moldova (Republica Moldova)",
38915         "md",
38916         "373"
38917       ],
38918       [
38919         "Monaco",
38920         "mc",
38921         "377"
38922       ],
38923       [
38924         "Mongolia (Монгол)",
38925         "mn",
38926         "976"
38927       ],
38928       [
38929         "Montenegro (Crna Gora)",
38930         "me",
38931         "382"
38932       ],
38933       [
38934         "Montserrat",
38935         "ms",
38936         "1664"
38937       ],
38938       [
38939         "Morocco (‫المغرب‬‎)",
38940         "ma",
38941         "212",
38942         0
38943       ],
38944       [
38945         "Mozambique (Moçambique)",
38946         "mz",
38947         "258"
38948       ],
38949       [
38950         "Myanmar (Burma) (မြန်မာ)",
38951         "mm",
38952         "95"
38953       ],
38954       [
38955         "Namibia (Namibië)",
38956         "na",
38957         "264"
38958       ],
38959       [
38960         "Nauru",
38961         "nr",
38962         "674"
38963       ],
38964       [
38965         "Nepal (नेपाल)",
38966         "np",
38967         "977"
38968       ],
38969       [
38970         "Netherlands (Nederland)",
38971         "nl",
38972         "31"
38973       ],
38974       [
38975         "New Caledonia (Nouvelle-Calédonie)",
38976         "nc",
38977         "687"
38978       ],
38979       [
38980         "New Zealand",
38981         "nz",
38982         "64"
38983       ],
38984       [
38985         "Nicaragua",
38986         "ni",
38987         "505"
38988       ],
38989       [
38990         "Niger (Nijar)",
38991         "ne",
38992         "227"
38993       ],
38994       [
38995         "Nigeria",
38996         "ng",
38997         "234"
38998       ],
38999       [
39000         "Niue",
39001         "nu",
39002         "683"
39003       ],
39004       [
39005         "Norfolk Island",
39006         "nf",
39007         "672"
39008       ],
39009       [
39010         "North Korea (조선 민주주의 인민 공화국)",
39011         "kp",
39012         "850"
39013       ],
39014       [
39015         "Northern Mariana Islands",
39016         "mp",
39017         "1670"
39018       ],
39019       [
39020         "Norway (Norge)",
39021         "no",
39022         "47",
39023         0
39024       ],
39025       [
39026         "Oman (‫عُمان‬‎)",
39027         "om",
39028         "968"
39029       ],
39030       [
39031         "Pakistan (‫پاکستان‬‎)",
39032         "pk",
39033         "92"
39034       ],
39035       [
39036         "Palau",
39037         "pw",
39038         "680"
39039       ],
39040       [
39041         "Palestine (‫فلسطين‬‎)",
39042         "ps",
39043         "970"
39044       ],
39045       [
39046         "Panama (Panamá)",
39047         "pa",
39048         "507"
39049       ],
39050       [
39051         "Papua New Guinea",
39052         "pg",
39053         "675"
39054       ],
39055       [
39056         "Paraguay",
39057         "py",
39058         "595"
39059       ],
39060       [
39061         "Peru (Perú)",
39062         "pe",
39063         "51"
39064       ],
39065       [
39066         "Philippines",
39067         "ph",
39068         "63"
39069       ],
39070       [
39071         "Poland (Polska)",
39072         "pl",
39073         "48"
39074       ],
39075       [
39076         "Portugal",
39077         "pt",
39078         "351"
39079       ],
39080       [
39081         "Puerto Rico",
39082         "pr",
39083         "1",
39084         3,
39085         ["787", "939"]
39086       ],
39087       [
39088         "Qatar (‫قطر‬‎)",
39089         "qa",
39090         "974"
39091       ],
39092       [
39093         "Réunion (La Réunion)",
39094         "re",
39095         "262",
39096         0
39097       ],
39098       [
39099         "Romania (România)",
39100         "ro",
39101         "40"
39102       ],
39103       [
39104         "Russia (Россия)",
39105         "ru",
39106         "7",
39107         0
39108       ],
39109       [
39110         "Rwanda",
39111         "rw",
39112         "250"
39113       ],
39114       [
39115         "Saint Barthélemy",
39116         "bl",
39117         "590",
39118         1
39119       ],
39120       [
39121         "Saint Helena",
39122         "sh",
39123         "290"
39124       ],
39125       [
39126         "Saint Kitts and Nevis",
39127         "kn",
39128         "1869"
39129       ],
39130       [
39131         "Saint Lucia",
39132         "lc",
39133         "1758"
39134       ],
39135       [
39136         "Saint Martin (Saint-Martin (partie française))",
39137         "mf",
39138         "590",
39139         2
39140       ],
39141       [
39142         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39143         "pm",
39144         "508"
39145       ],
39146       [
39147         "Saint Vincent and the Grenadines",
39148         "vc",
39149         "1784"
39150       ],
39151       [
39152         "Samoa",
39153         "ws",
39154         "685"
39155       ],
39156       [
39157         "San Marino",
39158         "sm",
39159         "378"
39160       ],
39161       [
39162         "São Tomé and Príncipe (São Tomé e Príncipe)",
39163         "st",
39164         "239"
39165       ],
39166       [
39167         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39168         "sa",
39169         "966"
39170       ],
39171       [
39172         "Senegal (Sénégal)",
39173         "sn",
39174         "221"
39175       ],
39176       [
39177         "Serbia (Србија)",
39178         "rs",
39179         "381"
39180       ],
39181       [
39182         "Seychelles",
39183         "sc",
39184         "248"
39185       ],
39186       [
39187         "Sierra Leone",
39188         "sl",
39189         "232"
39190       ],
39191       [
39192         "Singapore",
39193         "sg",
39194         "65"
39195       ],
39196       [
39197         "Sint Maarten",
39198         "sx",
39199         "1721"
39200       ],
39201       [
39202         "Slovakia (Slovensko)",
39203         "sk",
39204         "421"
39205       ],
39206       [
39207         "Slovenia (Slovenija)",
39208         "si",
39209         "386"
39210       ],
39211       [
39212         "Solomon Islands",
39213         "sb",
39214         "677"
39215       ],
39216       [
39217         "Somalia (Soomaaliya)",
39218         "so",
39219         "252"
39220       ],
39221       [
39222         "South Africa",
39223         "za",
39224         "27"
39225       ],
39226       [
39227         "South Korea (대한민국)",
39228         "kr",
39229         "82"
39230       ],
39231       [
39232         "South Sudan (‫جنوب السودان‬‎)",
39233         "ss",
39234         "211"
39235       ],
39236       [
39237         "Spain (España)",
39238         "es",
39239         "34"
39240       ],
39241       [
39242         "Sri Lanka (ශ්‍රී ලංකාව)",
39243         "lk",
39244         "94"
39245       ],
39246       [
39247         "Sudan (‫السودان‬‎)",
39248         "sd",
39249         "249"
39250       ],
39251       [
39252         "Suriname",
39253         "sr",
39254         "597"
39255       ],
39256       [
39257         "Svalbard and Jan Mayen",
39258         "sj",
39259         "47",
39260         1
39261       ],
39262       [
39263         "Swaziland",
39264         "sz",
39265         "268"
39266       ],
39267       [
39268         "Sweden (Sverige)",
39269         "se",
39270         "46"
39271       ],
39272       [
39273         "Switzerland (Schweiz)",
39274         "ch",
39275         "41"
39276       ],
39277       [
39278         "Syria (‫سوريا‬‎)",
39279         "sy",
39280         "963"
39281       ],
39282       [
39283         "Taiwan (台灣)",
39284         "tw",
39285         "886"
39286       ],
39287       [
39288         "Tajikistan",
39289         "tj",
39290         "992"
39291       ],
39292       [
39293         "Tanzania",
39294         "tz",
39295         "255"
39296       ],
39297       [
39298         "Thailand (ไทย)",
39299         "th",
39300         "66"
39301       ],
39302       [
39303         "Timor-Leste",
39304         "tl",
39305         "670"
39306       ],
39307       [
39308         "Togo",
39309         "tg",
39310         "228"
39311       ],
39312       [
39313         "Tokelau",
39314         "tk",
39315         "690"
39316       ],
39317       [
39318         "Tonga",
39319         "to",
39320         "676"
39321       ],
39322       [
39323         "Trinidad and Tobago",
39324         "tt",
39325         "1868"
39326       ],
39327       [
39328         "Tunisia (‫تونس‬‎)",
39329         "tn",
39330         "216"
39331       ],
39332       [
39333         "Turkey (Türkiye)",
39334         "tr",
39335         "90"
39336       ],
39337       [
39338         "Turkmenistan",
39339         "tm",
39340         "993"
39341       ],
39342       [
39343         "Turks and Caicos Islands",
39344         "tc",
39345         "1649"
39346       ],
39347       [
39348         "Tuvalu",
39349         "tv",
39350         "688"
39351       ],
39352       [
39353         "U.S. Virgin Islands",
39354         "vi",
39355         "1340"
39356       ],
39357       [
39358         "Uganda",
39359         "ug",
39360         "256"
39361       ],
39362       [
39363         "Ukraine (Україна)",
39364         "ua",
39365         "380"
39366       ],
39367       [
39368         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39369         "ae",
39370         "971"
39371       ],
39372       [
39373         "United Kingdom",
39374         "gb",
39375         "44",
39376         0
39377       ],
39378       [
39379         "United States",
39380         "us",
39381         "1",
39382         0
39383       ],
39384       [
39385         "Uruguay",
39386         "uy",
39387         "598"
39388       ],
39389       [
39390         "Uzbekistan (Oʻzbekiston)",
39391         "uz",
39392         "998"
39393       ],
39394       [
39395         "Vanuatu",
39396         "vu",
39397         "678"
39398       ],
39399       [
39400         "Vatican City (Città del Vaticano)",
39401         "va",
39402         "39",
39403         1
39404       ],
39405       [
39406         "Venezuela",
39407         "ve",
39408         "58"
39409       ],
39410       [
39411         "Vietnam (Việt Nam)",
39412         "vn",
39413         "84"
39414       ],
39415       [
39416         "Wallis and Futuna (Wallis-et-Futuna)",
39417         "wf",
39418         "681"
39419       ],
39420       [
39421         "Western Sahara (‫الصحراء الغربية‬‎)",
39422         "eh",
39423         "212",
39424         1
39425       ],
39426       [
39427         "Yemen (‫اليمن‬‎)",
39428         "ye",
39429         "967"
39430       ],
39431       [
39432         "Zambia",
39433         "zm",
39434         "260"
39435       ],
39436       [
39437         "Zimbabwe",
39438         "zw",
39439         "263"
39440       ],
39441       [
39442         "Åland Islands",
39443         "ax",
39444         "358",
39445         1
39446       ]
39447   ];
39448   
39449   return d;
39450 }/**
39451 *    This script refer to:
39452 *    Title: International Telephone Input
39453 *    Author: Jack O'Connor
39454 *    Code version:  v12.1.12
39455 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39456 **/
39457
39458 /**
39459  * @class Roo.bootstrap.PhoneInput
39460  * @extends Roo.bootstrap.TriggerField
39461  * An input with International dial-code selection
39462  
39463  * @cfg {String} defaultDialCode default '+852'
39464  * @cfg {Array} preferedCountries default []
39465   
39466  * @constructor
39467  * Create a new PhoneInput.
39468  * @param {Object} config Configuration options
39469  */
39470
39471 Roo.bootstrap.PhoneInput = function(config) {
39472     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39473 };
39474
39475 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39476         
39477         listWidth: undefined,
39478         
39479         selectedClass: 'active',
39480         
39481         invalidClass : "has-warning",
39482         
39483         validClass: 'has-success',
39484         
39485         allowed: '0123456789',
39486         
39487         /**
39488          * @cfg {String} defaultDialCode The default dial code when initializing the input
39489          */
39490         defaultDialCode: '+852',
39491         
39492         /**
39493          * @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
39494          */
39495         preferedCountries: false,
39496         
39497         getAutoCreate : function()
39498         {
39499             var data = Roo.bootstrap.PhoneInputData();
39500             var align = this.labelAlign || this.parentLabelAlign();
39501             var id = Roo.id();
39502             
39503             this.allCountries = [];
39504             this.dialCodeMapping = [];
39505             
39506             for (var i = 0; i < data.length; i++) {
39507               var c = data[i];
39508               this.allCountries[i] = {
39509                 name: c[0],
39510                 iso2: c[1],
39511                 dialCode: c[2],
39512                 priority: c[3] || 0,
39513                 areaCodes: c[4] || null
39514               };
39515               this.dialCodeMapping[c[2]] = {
39516                   name: c[0],
39517                   iso2: c[1],
39518                   priority: c[3] || 0,
39519                   areaCodes: c[4] || null
39520               };
39521             }
39522             
39523             var cfg = {
39524                 cls: 'form-group',
39525                 cn: []
39526             };
39527             
39528             var input =  {
39529                 tag: 'input',
39530                 id : id,
39531                 cls : 'form-control tel-input',
39532                 autocomplete: 'new-password'
39533             };
39534             
39535             var hiddenInput = {
39536                 tag: 'input',
39537                 type: 'hidden',
39538                 cls: 'hidden-tel-input'
39539             };
39540             
39541             if (this.name) {
39542                 hiddenInput.name = this.name;
39543             }
39544             
39545             if (this.disabled) {
39546                 input.disabled = true;
39547             }
39548             
39549             var flag_container = {
39550                 tag: 'div',
39551                 cls: 'flag-box',
39552                 cn: [
39553                     {
39554                         tag: 'div',
39555                         cls: 'flag'
39556                     },
39557                     {
39558                         tag: 'div',
39559                         cls: 'caret'
39560                     }
39561                 ]
39562             };
39563             
39564             var box = {
39565                 tag: 'div',
39566                 cls: this.hasFeedback ? 'has-feedback' : '',
39567                 cn: [
39568                     hiddenInput,
39569                     input,
39570                     {
39571                         tag: 'input',
39572                         cls: 'dial-code-holder',
39573                         disabled: true
39574                     }
39575                 ]
39576             };
39577             
39578             var container = {
39579                 cls: 'roo-select2-container input-group',
39580                 cn: [
39581                     flag_container,
39582                     box
39583                 ]
39584             };
39585             
39586             if (this.fieldLabel.length) {
39587                 var indicator = {
39588                     tag: 'i',
39589                     tooltip: 'This field is required'
39590                 };
39591                 
39592                 var label = {
39593                     tag: 'label',
39594                     'for':  id,
39595                     cls: 'control-label',
39596                     cn: []
39597                 };
39598                 
39599                 var label_text = {
39600                     tag: 'span',
39601                     html: this.fieldLabel
39602                 };
39603                 
39604                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39605                 label.cn = [
39606                     indicator,
39607                     label_text
39608                 ];
39609                 
39610                 if(this.indicatorpos == 'right') {
39611                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39612                     label.cn = [
39613                         label_text,
39614                         indicator
39615                     ];
39616                 }
39617                 
39618                 if(align == 'left') {
39619                     container = {
39620                         tag: 'div',
39621                         cn: [
39622                             container
39623                         ]
39624                     };
39625                     
39626                     if(this.labelWidth > 12){
39627                         label.style = "width: " + this.labelWidth + 'px';
39628                     }
39629                     if(this.labelWidth < 13 && this.labelmd == 0){
39630                         this.labelmd = this.labelWidth;
39631                     }
39632                     if(this.labellg > 0){
39633                         label.cls += ' col-lg-' + this.labellg;
39634                         input.cls += ' col-lg-' + (12 - this.labellg);
39635                     }
39636                     if(this.labelmd > 0){
39637                         label.cls += ' col-md-' + this.labelmd;
39638                         container.cls += ' col-md-' + (12 - this.labelmd);
39639                     }
39640                     if(this.labelsm > 0){
39641                         label.cls += ' col-sm-' + this.labelsm;
39642                         container.cls += ' col-sm-' + (12 - this.labelsm);
39643                     }
39644                     if(this.labelxs > 0){
39645                         label.cls += ' col-xs-' + this.labelxs;
39646                         container.cls += ' col-xs-' + (12 - this.labelxs);
39647                     }
39648                 }
39649             }
39650             
39651             cfg.cn = [
39652                 label,
39653                 container
39654             ];
39655             
39656             var settings = this;
39657             
39658             ['xs','sm','md','lg'].map(function(size){
39659                 if (settings[size]) {
39660                     cfg.cls += ' col-' + size + '-' + settings[size];
39661                 }
39662             });
39663             
39664             this.store = new Roo.data.Store({
39665                 proxy : new Roo.data.MemoryProxy({}),
39666                 reader : new Roo.data.JsonReader({
39667                     fields : [
39668                         {
39669                             'name' : 'name',
39670                             'type' : 'string'
39671                         },
39672                         {
39673                             'name' : 'iso2',
39674                             'type' : 'string'
39675                         },
39676                         {
39677                             'name' : 'dialCode',
39678                             'type' : 'string'
39679                         },
39680                         {
39681                             'name' : 'priority',
39682                             'type' : 'string'
39683                         },
39684                         {
39685                             'name' : 'areaCodes',
39686                             'type' : 'string'
39687                         }
39688                     ]
39689                 })
39690             });
39691             
39692             if(!this.preferedCountries) {
39693                 this.preferedCountries = [
39694                     'hk',
39695                     'gb',
39696                     'us'
39697                 ];
39698             }
39699             
39700             var p = this.preferedCountries.reverse();
39701             
39702             if(p) {
39703                 for (var i = 0; i < p.length; i++) {
39704                     for (var j = 0; j < this.allCountries.length; j++) {
39705                         if(this.allCountries[j].iso2 == p[i]) {
39706                             var t = this.allCountries[j];
39707                             this.allCountries.splice(j,1);
39708                             this.allCountries.unshift(t);
39709                         }
39710                     } 
39711                 }
39712             }
39713             
39714             this.store.proxy.data = {
39715                 success: true,
39716                 data: this.allCountries
39717             };
39718             
39719             return cfg;
39720         },
39721         
39722         initEvents : function()
39723         {
39724             this.createList();
39725             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39726             
39727             this.indicator = this.indicatorEl();
39728             this.flag = this.flagEl();
39729             this.dialCodeHolder = this.dialCodeHolderEl();
39730             
39731             this.trigger = this.el.select('div.flag-box',true).first();
39732             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39733             
39734             var _this = this;
39735             
39736             (function(){
39737                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39738                 _this.list.setWidth(lw);
39739             }).defer(100);
39740             
39741             this.list.on('mouseover', this.onViewOver, this);
39742             this.list.on('mousemove', this.onViewMove, this);
39743             this.inputEl().on("keyup", this.onKeyUp, this);
39744             
39745             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39746
39747             this.view = new Roo.View(this.list, this.tpl, {
39748                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39749             });
39750             
39751             this.view.on('click', this.onViewClick, this);
39752             this.setValue(this.defaultDialCode);
39753         },
39754         
39755         onTriggerClick : function(e)
39756         {
39757             Roo.log('trigger click');
39758             if(this.disabled){
39759                 return;
39760             }
39761             
39762             if(this.isExpanded()){
39763                 this.collapse();
39764                 this.hasFocus = false;
39765             }else {
39766                 this.store.load({});
39767                 this.hasFocus = true;
39768                 this.expand();
39769             }
39770         },
39771         
39772         isExpanded : function()
39773         {
39774             return this.list.isVisible();
39775         },
39776         
39777         collapse : function()
39778         {
39779             if(!this.isExpanded()){
39780                 return;
39781             }
39782             this.list.hide();
39783             Roo.get(document).un('mousedown', this.collapseIf, this);
39784             Roo.get(document).un('mousewheel', this.collapseIf, this);
39785             this.fireEvent('collapse', this);
39786             this.validate();
39787         },
39788         
39789         expand : function()
39790         {
39791             Roo.log('expand');
39792
39793             if(this.isExpanded() || !this.hasFocus){
39794                 return;
39795             }
39796             
39797             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39798             this.list.setWidth(lw);
39799             
39800             this.list.show();
39801             this.restrictHeight();
39802             
39803             Roo.get(document).on('mousedown', this.collapseIf, this);
39804             Roo.get(document).on('mousewheel', this.collapseIf, this);
39805             
39806             this.fireEvent('expand', this);
39807         },
39808         
39809         restrictHeight : function()
39810         {
39811             this.list.alignTo(this.inputEl(), this.listAlign);
39812             this.list.alignTo(this.inputEl(), this.listAlign);
39813         },
39814         
39815         onViewOver : function(e, t)
39816         {
39817             if(this.inKeyMode){
39818                 return;
39819             }
39820             var item = this.view.findItemFromChild(t);
39821             
39822             if(item){
39823                 var index = this.view.indexOf(item);
39824                 this.select(index, false);
39825             }
39826         },
39827
39828         // private
39829         onViewClick : function(view, doFocus, el, e)
39830         {
39831             var index = this.view.getSelectedIndexes()[0];
39832             
39833             var r = this.store.getAt(index);
39834             
39835             if(r){
39836                 this.onSelect(r, index);
39837             }
39838             if(doFocus !== false && !this.blockFocus){
39839                 this.inputEl().focus();
39840             }
39841         },
39842         
39843         onViewMove : function(e, t)
39844         {
39845             this.inKeyMode = false;
39846         },
39847         
39848         select : function(index, scrollIntoView)
39849         {
39850             this.selectedIndex = index;
39851             this.view.select(index);
39852             if(scrollIntoView !== false){
39853                 var el = this.view.getNode(index);
39854                 if(el){
39855                     this.list.scrollChildIntoView(el, false);
39856                 }
39857             }
39858         },
39859         
39860         createList : function()
39861         {
39862             this.list = Roo.get(document.body).createChild({
39863                 tag: 'ul',
39864                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39865                 style: 'display:none'
39866             });
39867             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39868         },
39869         
39870         collapseIf : function(e)
39871         {
39872             var in_combo  = e.within(this.el);
39873             var in_list =  e.within(this.list);
39874             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39875             
39876             if (in_combo || in_list || is_list) {
39877                 return;
39878             }
39879             this.collapse();
39880         },
39881         
39882         onSelect : function(record, index)
39883         {
39884             if(this.fireEvent('beforeselect', this, record, index) !== false){
39885                 
39886                 this.setFlagClass(record.data.iso2);
39887                 this.setDialCode(record.data.dialCode);
39888                 this.hasFocus = false;
39889                 this.collapse();
39890                 this.fireEvent('select', this, record, index);
39891             }
39892         },
39893         
39894         flagEl : function()
39895         {
39896             var flag = this.el.select('div.flag',true).first();
39897             if(!flag){
39898                 return false;
39899             }
39900             return flag;
39901         },
39902         
39903         dialCodeHolderEl : function()
39904         {
39905             var d = this.el.select('input.dial-code-holder',true).first();
39906             if(!d){
39907                 return false;
39908             }
39909             return d;
39910         },
39911         
39912         setDialCode : function(v)
39913         {
39914             this.dialCodeHolder.dom.value = '+'+v;
39915         },
39916         
39917         setFlagClass : function(n)
39918         {
39919             this.flag.dom.className = 'flag '+n;
39920         },
39921         
39922         getValue : function()
39923         {
39924             var v = this.inputEl().getValue();
39925             if(this.dialCodeHolder) {
39926                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39927             }
39928             return v;
39929         },
39930         
39931         setValue : function(v)
39932         {
39933             var d = this.getDialCode(v);
39934             
39935             //invalid dial code
39936             if(v.length == 0 || !d || d.length == 0) {
39937                 if(this.rendered){
39938                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39939                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39940                 }
39941                 return;
39942             }
39943             
39944             //valid dial code
39945             this.setFlagClass(this.dialCodeMapping[d].iso2);
39946             this.setDialCode(d);
39947             this.inputEl().dom.value = v.replace('+'+d,'');
39948             this.hiddenEl().dom.value = this.getValue();
39949             
39950             this.validate();
39951         },
39952         
39953         getDialCode : function(v = '')
39954         {
39955             if (v.length == 0) {
39956                 return this.dialCodeHolder.dom.value;
39957             }
39958             
39959             var dialCode = "";
39960             if (v.charAt(0) != "+") {
39961                 return false;
39962             }
39963             var numericChars = "";
39964             for (var i = 1; i < v.length; i++) {
39965               var c = v.charAt(i);
39966               if (!isNaN(c)) {
39967                 numericChars += c;
39968                 if (this.dialCodeMapping[numericChars]) {
39969                   dialCode = v.substr(1, i);
39970                 }
39971                 if (numericChars.length == 4) {
39972                   break;
39973                 }
39974               }
39975             }
39976             return dialCode;
39977         },
39978         
39979         reset : function()
39980         {
39981             this.setValue(this.defaultDialCode);
39982             this.validate();
39983         },
39984         
39985         hiddenEl : function()
39986         {
39987             return this.el.select('input.hidden-tel-input',true).first();
39988         },
39989         
39990         onKeyUp : function(e){
39991             
39992             var k = e.getKey();
39993             var c = e.getCharCode();
39994             
39995             if(
39996                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39997                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39998             ){
39999                 e.stopEvent();
40000             }
40001             
40002             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40003             //     return;
40004             // }
40005             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40006                 e.stopEvent();
40007             }
40008             
40009             this.setValue(this.getValue());
40010         }
40011         
40012 });
40013 /**
40014  * @class Roo.bootstrap.MoneyField
40015  * @extends Roo.bootstrap.ComboBox
40016  * Bootstrap MoneyField class
40017  * 
40018  * @constructor
40019  * Create a new MoneyField.
40020  * @param {Object} config Configuration options
40021  */
40022
40023 Roo.bootstrap.MoneyField = function(config) {
40024     
40025     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40026     
40027 };
40028
40029 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40030     
40031     /**
40032      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40033      */
40034     allowDecimals : true,
40035     /**
40036      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40037      */
40038     decimalSeparator : ".",
40039     /**
40040      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40041      */
40042     decimalPrecision : 2,
40043     /**
40044      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40045      */
40046     allowNegative : true,
40047     /**
40048      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40049      */
40050     minValue : Number.NEGATIVE_INFINITY,
40051     /**
40052      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40053      */
40054     maxValue : Number.MAX_VALUE,
40055     /**
40056      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40057      */
40058     minText : "The minimum value for this field is {0}",
40059     /**
40060      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40061      */
40062     maxText : "The maximum value for this field is {0}",
40063     /**
40064      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40065      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40066      */
40067     nanText : "{0} is not a valid number",
40068     /**
40069      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40070      */
40071     castInt : true,
40072     /**
40073      * @cfg {String} defaults currency of the MoneyField
40074      * value should be in lkey
40075      */
40076     defaultCurrency : false,
40077     
40078     
40079     inputlg : 9,
40080     inputmd : 9,
40081     inputsm : 9,
40082     inputxs : 6,
40083     
40084     store : false,
40085     
40086     getAutoCreate : function()
40087     {
40088         var align = this.labelAlign || this.parentLabelAlign();
40089         
40090         var id = Roo.id();
40091
40092         var cfg = {
40093             cls: 'form-group',
40094             cn: []
40095         };
40096
40097         var input =  {
40098             tag: 'input',
40099             id : id,
40100             cls : 'form-control roo-money-amount-input',
40101             autocomplete: 'new-password'
40102         };
40103         
40104         if (this.name) {
40105             input.name = this.name;
40106         }
40107
40108         if (this.disabled) {
40109             input.disabled = true;
40110         }
40111
40112         var clg = 12 - this.inputlg;
40113         var cmd = 12 - this.inputmd;
40114         var csm = 12 - this.inputsm;
40115         var cxs = 12 - this.inputxs;
40116         
40117         var container = {
40118             tag : 'div',
40119             cls : 'row roo-money-field',
40120             cn : [
40121                 {
40122                     tag : 'div',
40123                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40124                     cn : [
40125                         {
40126                             tag : 'div',
40127                             cls: 'roo-select2-container input-group',
40128                             cn: [
40129                                 {
40130                                     tag : 'input',
40131                                     cls : 'form-control roo-money-currency-input',
40132                                     autocomplete: 'new-password',
40133                                     readOnly : 1,
40134                                     name : this.currencyName
40135                                 },
40136                                 {
40137                                     tag :'span',
40138                                     cls : 'input-group-addon',
40139                                     cn : [
40140                                         {
40141                                             tag: 'span',
40142                                             cls: 'caret'
40143                                         }
40144                                     ]
40145                                 }
40146                             ]
40147                         }
40148                     ]
40149                 },
40150                 {
40151                     tag : 'div',
40152                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40153                     cn : [
40154                         {
40155                             tag: 'div',
40156                             cls: this.hasFeedback ? 'has-feedback' : '',
40157                             cn: [
40158                                 input
40159                             ]
40160                         }
40161                     ]
40162                 }
40163             ]
40164             
40165         };
40166         
40167         if (this.fieldLabel.length) {
40168             var indicator = {
40169                 tag: 'i',
40170                 tooltip: 'This field is required'
40171             };
40172
40173             var label = {
40174                 tag: 'label',
40175                 'for':  id,
40176                 cls: 'control-label',
40177                 cn: []
40178             };
40179
40180             var label_text = {
40181                 tag: 'span',
40182                 html: this.fieldLabel
40183             };
40184
40185             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40186             label.cn = [
40187                 indicator,
40188                 label_text
40189             ];
40190
40191             if(this.indicatorpos == 'right') {
40192                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40193                 label.cn = [
40194                     label_text,
40195                     indicator
40196                 ];
40197             }
40198
40199             if(align == 'left') {
40200                 container = {
40201                     tag: 'div',
40202                     cn: [
40203                         container
40204                     ]
40205                 };
40206
40207                 if(this.labelWidth > 12){
40208                     label.style = "width: " + this.labelWidth + 'px';
40209                 }
40210                 if(this.labelWidth < 13 && this.labelmd == 0){
40211                     this.labelmd = this.labelWidth;
40212                 }
40213                 if(this.labellg > 0){
40214                     label.cls += ' col-lg-' + this.labellg;
40215                     input.cls += ' col-lg-' + (12 - this.labellg);
40216                 }
40217                 if(this.labelmd > 0){
40218                     label.cls += ' col-md-' + this.labelmd;
40219                     container.cls += ' col-md-' + (12 - this.labelmd);
40220                 }
40221                 if(this.labelsm > 0){
40222                     label.cls += ' col-sm-' + this.labelsm;
40223                     container.cls += ' col-sm-' + (12 - this.labelsm);
40224                 }
40225                 if(this.labelxs > 0){
40226                     label.cls += ' col-xs-' + this.labelxs;
40227                     container.cls += ' col-xs-' + (12 - this.labelxs);
40228                 }
40229             }
40230         }
40231
40232         cfg.cn = [
40233             label,
40234             container
40235         ];
40236
40237         var settings = this;
40238
40239         ['xs','sm','md','lg'].map(function(size){
40240             if (settings[size]) {
40241                 cfg.cls += ' col-' + size + '-' + settings[size];
40242             }
40243         });
40244         
40245         return cfg;
40246         
40247     },
40248     
40249     initEvents : function()
40250     {
40251         this.indicator = this.indicatorEl();
40252         
40253         this.initCurrencyEvent();
40254         
40255         this.initNumberEvent();
40256         
40257     },
40258     
40259     initCurrencyEvent : function()
40260     {
40261         if (!this.store) {
40262             throw "can not find store for combo";
40263         }
40264         
40265         this.store = Roo.factory(this.store, Roo.data);
40266         this.store.parent = this;
40267         
40268         this.createList();
40269         
40270         this.triggerEl = this.el.select('.input-group-addon', true).first();
40271         
40272         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40273         
40274         var _this = this;
40275         
40276         (function(){
40277             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40278             _this.list.setWidth(lw);
40279         }).defer(100);
40280         
40281         this.list.on('mouseover', this.onViewOver, this);
40282         this.list.on('mousemove', this.onViewMove, this);
40283         this.list.on('scroll', this.onViewScroll, this);
40284         
40285         if(!this.tpl){
40286             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40287         }
40288         
40289         this.view = new Roo.View(this.list, this.tpl, {
40290             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40291         });
40292         
40293         this.view.on('click', this.onViewClick, this);
40294         
40295         this.store.on('beforeload', this.onBeforeLoad, this);
40296         this.store.on('load', this.onLoad, this);
40297         this.store.on('loadexception', this.onLoadException, this);
40298         
40299         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40300             "up" : function(e){
40301                 this.inKeyMode = true;
40302                 this.selectPrev();
40303             },
40304
40305             "down" : function(e){
40306                 if(!this.isExpanded()){
40307                     this.onTriggerClick();
40308                 }else{
40309                     this.inKeyMode = true;
40310                     this.selectNext();
40311                 }
40312             },
40313
40314             "enter" : function(e){
40315                 this.collapse();
40316                 
40317                 if(this.fireEvent("specialkey", this, e)){
40318                     this.onViewClick(false);
40319                 }
40320                 
40321                 return true;
40322             },
40323
40324             "esc" : function(e){
40325                 this.collapse();
40326             },
40327
40328             "tab" : function(e){
40329                 this.collapse();
40330                 
40331                 if(this.fireEvent("specialkey", this, e)){
40332                     this.onViewClick(false);
40333                 }
40334                 
40335                 return true;
40336             },
40337
40338             scope : this,
40339
40340             doRelay : function(foo, bar, hname){
40341                 if(hname == 'down' || this.scope.isExpanded()){
40342                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40343                 }
40344                 return true;
40345             },
40346
40347             forceKeyDown: true
40348         });
40349         
40350         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40351         
40352     },
40353     
40354     initNumberEvent : function(e)
40355     {
40356         this.inputEl().on("keydown" , this.fireKey,  this);
40357         this.inputEl().on("focus", this.onFocus,  this);
40358         this.inputEl().on("blur", this.onBlur,  this);
40359         
40360         this.inputEl().relayEvent('keyup', this);
40361         
40362         if(this.indicator){
40363             this.indicator.addClass('invisible');
40364         }
40365  
40366         this.originalValue = this.getValue();
40367         
40368         if(this.validationEvent == 'keyup'){
40369             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40370             this.inputEl().on('keyup', this.filterValidation, this);
40371         }
40372         else if(this.validationEvent !== false){
40373             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40374         }
40375         
40376         if(this.selectOnFocus){
40377             this.on("focus", this.preFocus, this);
40378             
40379         }
40380         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40381             this.inputEl().on("keypress", this.filterKeys, this);
40382         } else {
40383             this.inputEl().relayEvent('keypress', this);
40384         }
40385         
40386         var allowed = "0123456789";
40387         
40388         if(this.allowDecimals){
40389             allowed += this.decimalSeparator;
40390         }
40391         
40392         if(this.allowNegative){
40393             allowed += "-";
40394         }
40395         
40396         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40397         
40398         var keyPress = function(e){
40399             
40400             var k = e.getKey();
40401             
40402             var c = e.getCharCode();
40403             
40404             if(
40405                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40406                     allowed.indexOf(String.fromCharCode(c)) === -1
40407             ){
40408                 e.stopEvent();
40409                 return;
40410             }
40411             
40412             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40413                 return;
40414             }
40415             
40416             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40417                 e.stopEvent();
40418             }
40419         };
40420         
40421         this.inputEl().on("keypress", keyPress, this);
40422         
40423     },
40424     
40425     onTriggerClick : function(e)
40426     {   
40427         if(this.disabled){
40428             return;
40429         }
40430         
40431         this.page = 0;
40432         this.loadNext = false;
40433         
40434         if(this.isExpanded()){
40435             this.collapse();
40436             return;
40437         }
40438         
40439         this.hasFocus = true;
40440         
40441         if(this.triggerAction == 'all') {
40442             this.doQuery(this.allQuery, true);
40443             return;
40444         }
40445         
40446         this.doQuery(this.getRawValue());
40447     },
40448     
40449     getCurrency : function()
40450     {   
40451         var v = this.currencyEl().getValue();
40452         
40453         return v;
40454     },
40455     
40456     restrictHeight : function()
40457     {
40458         this.list.alignTo(this.currencyEl(), this.listAlign);
40459         this.list.alignTo(this.currencyEl(), this.listAlign);
40460     },
40461     
40462     onViewClick : function(view, doFocus, el, e)
40463     {
40464         var index = this.view.getSelectedIndexes()[0];
40465         
40466         var r = this.store.getAt(index);
40467         
40468         if(r){
40469             this.onSelect(r, index);
40470         }
40471     },
40472     
40473     onSelect : function(record, index){
40474         
40475         if(this.fireEvent('beforeselect', this, record, index) !== false){
40476         
40477             this.setFromCurrencyData(index > -1 ? record.data : false);
40478             
40479             this.collapse();
40480             
40481             this.fireEvent('select', this, record, index);
40482         }
40483     },
40484     
40485     setFromCurrencyData : function(o)
40486     {
40487         var currency = '';
40488         
40489         this.lastCurrency = o;
40490         
40491         if (this.currencyField) {
40492             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40493         } else {
40494             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40495         }
40496         
40497         this.lastSelectionText = currency;
40498         
40499         //setting default currency
40500         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40501             this.setCurrency(this.defaultCurrency);
40502             return;
40503         }
40504         
40505         this.setCurrency(currency);
40506     },
40507     
40508     setFromData : function(o)
40509     {
40510         var c = {};
40511         
40512         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40513         
40514         this.setFromCurrencyData(c);
40515         
40516         var value = '';
40517         
40518         if (this.name) {
40519             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40520         } else {
40521             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40522         }
40523         
40524         this.setValue(value);
40525         
40526     },
40527     
40528     setCurrency : function(v)
40529     {   
40530         this.currencyValue = v;
40531         
40532         if(this.rendered){
40533             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40534             this.validate();
40535         }
40536     },
40537     
40538     setValue : function(v)
40539     {
40540         v = this.fixPrecision(v);
40541         
40542         v = String(v).replace(".", this.decimalSeparator);
40543         
40544         this.value = v;
40545         
40546         if(this.rendered){
40547             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40548             this.validate();
40549         }
40550     },
40551     
40552     getRawValue : function()
40553     {
40554         var v = this.inputEl().getValue();
40555         
40556         return v;
40557     },
40558     
40559     getValue : function()
40560     {
40561         return this.fixPrecision(this.parseValue(this.getRawValue()));
40562     },
40563     
40564     parseValue : function(value)
40565     {
40566         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40567         return isNaN(value) ? '' : value;
40568     },
40569     
40570     fixPrecision : function(value)
40571     {
40572         var nan = isNaN(value);
40573         
40574         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40575             return nan ? '' : value;
40576         }
40577         
40578         return parseFloat(value).toFixed(this.decimalPrecision);
40579     },
40580     
40581     decimalPrecisionFcn : function(v)
40582     {
40583         return Math.floor(v);
40584     },
40585     
40586     validateValue : function(value)
40587     {
40588         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40589             return false;
40590         }
40591         
40592         var num = this.parseValue(value);
40593         
40594         if(isNaN(num)){
40595             this.markInvalid(String.format(this.nanText, value));
40596             return false;
40597         }
40598         
40599         if(num < this.minValue){
40600             this.markInvalid(String.format(this.minText, this.minValue));
40601             return false;
40602         }
40603         
40604         if(num > this.maxValue){
40605             this.markInvalid(String.format(this.maxText, this.maxValue));
40606             return false;
40607         }
40608         
40609         return true;
40610     },
40611     
40612     validate : function()
40613     {
40614         if(this.disabled || this.allowBlank){
40615             this.markValid();
40616             return true;
40617         }
40618         
40619         var currency = this.getCurrency();
40620         
40621         if(this.validateValue(this.getRawValue()) && currency.length){
40622             this.markValid();
40623             return true;
40624         }
40625         
40626         this.markInvalid();
40627         return false;
40628     },
40629     
40630     getName: function()
40631     {
40632         return this.name;
40633     },
40634     
40635     beforeBlur : function()
40636     {
40637         if(!this.castInt){
40638             return;
40639         }
40640         
40641         var v = this.parseValue(this.getRawValue());
40642         
40643         if(v){
40644             this.setValue(v);
40645         }
40646     },
40647     
40648     onBlur : function()
40649     {
40650         this.beforeBlur();
40651         
40652         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40653             //this.el.removeClass(this.focusClass);
40654         }
40655         
40656         this.hasFocus = false;
40657         
40658         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40659             this.validate();
40660         }
40661         
40662         var v = this.getValue();
40663         
40664         if(String(v) !== String(this.startValue)){
40665             this.fireEvent('change', this, v, this.startValue);
40666         }
40667         
40668         this.fireEvent("blur", this);
40669     },
40670     
40671     inputEl : function()
40672     {
40673         return this.el.select('.roo-money-amount-input', true).first();
40674     },
40675     
40676     currencyEl : function()
40677     {
40678         return this.el.select('.roo-money-currency-input', true).first();
40679     }
40680     
40681 });