4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
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];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
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);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @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)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.headerEl = this.el.select('.modal-header',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2574 this.footerEl = this.el.select('.modal-footer',true).first();
2576 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577 this.maskEl.enableDisplayMode("block");
2579 //this.el.addClass("x-dlg-modal");
2581 if (this.buttons.length) {
2582 Roo.each(this.buttons, function(bb) {
2583 var b = Roo.apply({}, bb);
2584 b.xns = b.xns || Roo.bootstrap;
2585 b.xtype = b.xtype || 'Button';
2586 if (typeof(b.listeners) == 'undefined') {
2587 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2590 var btn = Roo.factory(b);
2592 btn.render(this.el.select('.modal-footer div').first());
2596 // render the children.
2599 if(typeof(this.items) != 'undefined'){
2600 var items = this.items;
2603 for(var i =0;i < items.length;i++) {
2604 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2608 this.items = nitems;
2610 // where are these used - they used to be body/close/footer
2614 //this.el.addClass([this.fieldClass, this.cls]);
2618 getAutoCreate : function(){
2623 html : this.html || ''
2628 cls : 'modal-title',
2632 if(this.specificTitle){
2638 if (this.allow_close) {
2650 if(this.size.length){
2651 size = 'modal-' + this.size;
2656 style : 'display: none',
2659 cls: "modal-dialog " + size,
2662 cls : "modal-content",
2665 cls : 'modal-header',
2670 cls : 'modal-footer',
2674 cls: 'btn-' + this.buttonPosition
2691 modal.cls += ' fade';
2697 getChildContainer : function() {
2702 getButtonContainer : function() {
2703 return this.el.select('.modal-footer div',true).first();
2706 initEvents : function()
2708 if (this.allow_close) {
2709 this.closeEl.on('click', this.hide, this);
2711 Roo.EventManager.onWindowResize(this.resize, this, true);
2718 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2719 if (this.fitwindow) {
2720 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2726 setSize : function(w,h)
2736 if (!this.rendered) {
2740 this.el.setStyle('display', 'block');
2742 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2745 this.el.addClass('in');
2748 this.el.addClass('in');
2752 // not sure how we can show data in here..
2754 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2757 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2760 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2765 this.fireEvent('show', this);
2767 // set zindex here - otherwise it appears to be ignored...
2768 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2771 this.items.forEach( function(e) {
2772 e.layout ? e.layout() : false;
2780 if(this.fireEvent("beforehide", this) !== false){
2782 Roo.get(document.body).removeClass("x-body-masked");
2783 this.el.removeClass('in');
2784 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2786 if(this.animate){ // why
2788 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2790 this.el.setStyle('display', 'none');
2792 this.fireEvent('hide', this);
2796 addButton : function(str, cb)
2800 var b = Roo.apply({}, { html : str } );
2801 b.xns = b.xns || Roo.bootstrap;
2802 b.xtype = b.xtype || 'Button';
2803 if (typeof(b.listeners) == 'undefined') {
2804 b.listeners = { click : cb.createDelegate(this) };
2807 var btn = Roo.factory(b);
2809 btn.render(this.el.select('.modal-footer div').first());
2815 setDefaultButton : function(btn)
2817 //this.el.select('.modal-footer').()
2821 resizeTo: function(w,h)
2825 this.dialogEl.setWidth(w);
2826 if (this.diff === false) {
2827 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2830 this.bodyEl.setHeight(h-this.diff);
2834 setContentSize : function(w, h)
2838 onButtonClick: function(btn,e)
2841 this.fireEvent('btnclick', btn.name, e);
2844 * Set the title of the Dialog
2845 * @param {String} str new Title
2847 setTitle: function(str) {
2848 this.titleEl.dom.innerHTML = str;
2851 * Set the body of the Dialog
2852 * @param {String} str new Title
2854 setBody: function(str) {
2855 this.bodyEl.dom.innerHTML = str;
2858 * Set the body of the Dialog using the template
2859 * @param {Obj} data - apply this data to the template and replace the body contents.
2861 applyBody: function(obj)
2864 Roo.log("Error - using apply Body without a template");
2867 this.tmpl.overwrite(this.bodyEl, obj);
2873 Roo.apply(Roo.bootstrap.Modal, {
2875 * Button config that displays a single OK button
2884 * Button config that displays Yes and No buttons
2900 * Button config that displays OK and Cancel buttons
2915 * Button config that displays Yes, No and Cancel buttons
2939 * messagebox - can be used as a replace
2943 * @class Roo.MessageBox
2944 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2953 // process text value...
2957 // Show a dialog using config options:
2959 title:'Save Changes?',
2960 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961 buttons: Roo.Msg.YESNOCANCEL,
2968 Roo.bootstrap.MessageBox = function(){
2969 var dlg, opt, mask, waitTimer;
2970 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971 var buttons, activeTextEl, bwidth;
2975 var handleButton = function(button){
2977 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2981 var handleHide = function(){
2983 dlg.el.removeClass(opt.cls);
2986 // Roo.TaskMgr.stop(waitTimer);
2987 // waitTimer = null;
2992 var updateButtons = function(b){
2995 buttons["ok"].hide();
2996 buttons["cancel"].hide();
2997 buttons["yes"].hide();
2998 buttons["no"].hide();
2999 //dlg.footer.dom.style.display = 'none';
3002 dlg.footerEl.dom.style.display = '';
3003 for(var k in buttons){
3004 if(typeof buttons[k] != "function"){
3007 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008 width += buttons[k].el.getWidth()+15;
3018 var handleEsc = function(d, k, e){
3019 if(opt && opt.closable !== false){
3029 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030 * @return {Roo.BasicDialog} The BasicDialog element
3032 getDialog : function(){
3034 dlg = new Roo.bootstrap.Modal( {
3037 //constraintoviewport:false,
3039 //collapsible : false,
3044 //buttonAlign:"center",
3045 closeClick : function(){
3046 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3049 handleButton("cancel");
3054 dlg.on("hide", handleHide);
3056 //dlg.addKeyListener(27, handleEsc);
3058 this.buttons = buttons;
3059 var bt = this.buttonText;
3060 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3065 bodyEl = dlg.bodyEl.createChild({
3067 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068 '<textarea class="roo-mb-textarea"></textarea>' +
3069 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3071 msgEl = bodyEl.dom.firstChild;
3072 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073 textboxEl.enableDisplayMode();
3074 textboxEl.addKeyListener([10,13], function(){
3075 if(dlg.isVisible() && opt && opt.buttons){
3078 }else if(opt.buttons.yes){
3079 handleButton("yes");
3083 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084 textareaEl.enableDisplayMode();
3085 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086 progressEl.enableDisplayMode();
3088 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089 //var pf = progressEl.dom.firstChild;
3091 //pp = Roo.get(pf.firstChild);
3092 //pp.setHeight(pf.offsetHeight);
3100 * Updates the message box body text
3101 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102 * the XHTML-compliant non-breaking space character '&#160;')
3103 * @return {Roo.MessageBox} This message box
3105 updateText : function(text)
3107 if(!dlg.isVisible() && !opt.width){
3108 dlg.dialogEl.setWidth(this.maxWidth);
3109 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3111 msgEl.innerHTML = text || ' ';
3113 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3116 Math.min(opt.width || cw , this.maxWidth),
3117 Math.max(opt.minWidth || this.minWidth, bwidth)
3120 activeTextEl.setWidth(w);
3122 if(dlg.isVisible()){
3123 dlg.fixedcenter = false;
3125 // to big, make it scroll. = But as usual stupid IE does not support
3128 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3132 bodyEl.dom.style.height = '';
3133 bodyEl.dom.style.overflowY = '';
3136 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3138 bodyEl.dom.style.overflowX = '';
3141 dlg.setContentSize(w, bodyEl.getHeight());
3142 if(dlg.isVisible()){
3143 dlg.fixedcenter = true;
3149 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3150 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153 * @return {Roo.MessageBox} This message box
3155 updateProgress : function(value, text){
3157 this.updateText(text);
3159 if (pp) { // weird bug on my firefox - for some reason this is not defined
3160 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3166 * Returns true if the message box is currently displayed
3167 * @return {Boolean} True if the message box is visible, else false
3169 isVisible : function(){
3170 return dlg && dlg.isVisible();
3174 * Hides the message box if it is displayed
3177 if(this.isVisible()){
3183 * Displays a new message box, or reinitializes an existing message box, based on the config options
3184 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185 * The following config object properties are supported:
3187 Property Type Description
3188 ---------- --------------- ------------------------------------------------------------------------------------
3189 animEl String/Element An id or Element from which the message box should animate as it opens and
3190 closes (defaults to undefined)
3191 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable Boolean False to hide the top-right close button (defaults to true). Note that
3194 progress and wait dialogs will ignore this property and always hide the
3195 close button as they can only be closed programmatically.
3196 cls String A custom CSS class to apply to the message box element
3197 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3198 displayed (defaults to 75)
3199 fn Function A callback function to execute after closing the dialog. The arguments to the
3200 function will be btn (the name of the button that was clicked, if applicable,
3201 e.g. "ok"), and text (the value of the active text field, if applicable).
3202 Progress and wait dialogs will ignore this option since they do not respond to
3203 user actions and can only be closed programmatically, so any required function
3204 should be called by the same code after it closes the dialog.
3205 icon String A CSS class that provides a background image to be used as an icon for
3206 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3208 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3209 modal Boolean False to allow user interaction with the page while the message box is
3210 displayed (defaults to true)
3211 msg String A string that will replace the existing message box body text (defaults
3212 to the XHTML-compliant non-breaking space character ' ')
3213 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3214 progress Boolean True to display a progress bar (defaults to false)
3215 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3218 title String The title text
3219 value String The string value to set into the active textbox element if displayed
3220 wait Boolean True to display a progress bar (defaults to false)
3221 width Number The width of the dialog in pixels
3228 msg: 'Please enter your address:',
3230 buttons: Roo.MessageBox.OKCANCEL,
3233 animEl: 'addAddressBtn'
3236 * @param {Object} config Configuration options
3237 * @return {Roo.MessageBox} This message box
3239 show : function(options)
3242 // this causes nightmares if you show one dialog after another
3243 // especially on callbacks..
3245 if(this.isVisible()){
3248 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3250 Roo.log("New Dialog Message:" + options.msg )
3251 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3255 var d = this.getDialog();
3257 d.setTitle(opt.title || " ");
3258 d.closeEl.setDisplayed(opt.closable !== false);
3259 activeTextEl = textboxEl;
3260 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3265 textareaEl.setHeight(typeof opt.multiline == "number" ?
3266 opt.multiline : this.defaultTextHeight);
3267 activeTextEl = textareaEl;
3276 progressEl.setDisplayed(opt.progress === true);
3277 this.updateProgress(0);
3278 activeTextEl.dom.value = opt.value || "";
3280 dlg.setDefaultButton(activeTextEl);
3282 var bs = opt.buttons;
3286 }else if(bs && bs.yes){
3287 db = buttons["yes"];
3289 dlg.setDefaultButton(db);
3291 bwidth = updateButtons(opt.buttons);
3292 this.updateText(opt.msg);
3294 d.el.addClass(opt.cls);
3296 d.proxyDrag = opt.proxyDrag === true;
3297 d.modal = opt.modal !== false;
3298 d.mask = opt.modal !== false ? mask : false;
3300 // force it to the end of the z-index stack so it gets a cursor in FF
3301 document.body.appendChild(dlg.el.dom);
3302 d.animateTarget = null;
3303 d.show(options.animEl);
3309 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3310 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311 * and closing the message box when the process is complete.
3312 * @param {String} title The title bar text
3313 * @param {String} msg The message box body text
3314 * @return {Roo.MessageBox} This message box
3316 progress : function(title, msg){
3323 minWidth: this.minProgressWidth,
3330 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331 * If a callback function is passed it will be called after the user clicks the button, and the
3332 * id of the button that was clicked will be passed as the only parameter to the callback
3333 * (could also be the top-right close button).
3334 * @param {String} title The title bar text
3335 * @param {String} msg The message box body text
3336 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337 * @param {Object} scope (optional) The scope of the callback function
3338 * @return {Roo.MessageBox} This message box
3340 alert : function(title, msg, fn, scope)
3355 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3356 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357 * You are responsible for closing the message box when the process is complete.
3358 * @param {String} msg The message box body text
3359 * @param {String} title (optional) The title bar text
3360 * @return {Roo.MessageBox} This message box
3362 wait : function(msg, title){
3373 waitTimer = Roo.TaskMgr.start({
3375 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3383 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386 * @param {String} title The title bar text
3387 * @param {String} msg The message box body text
3388 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389 * @param {Object} scope (optional) The scope of the callback function
3390 * @return {Roo.MessageBox} This message box
3392 confirm : function(title, msg, fn, scope){
3396 buttons: this.YESNO,
3405 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3407 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408 * (could also be the top-right close button) and the text that was entered will be passed as the two
3409 * parameters to the callback.
3410 * @param {String} title The title bar text
3411 * @param {String} msg The message box body text
3412 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413 * @param {Object} scope (optional) The scope of the callback function
3414 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416 * @return {Roo.MessageBox} This message box
3418 prompt : function(title, msg, fn, scope, multiline){
3422 buttons: this.OKCANCEL,
3427 multiline: multiline,
3434 * Button config that displays a single OK button
3439 * Button config that displays Yes and No buttons
3442 YESNO : {yes:true, no:true},
3444 * Button config that displays OK and Cancel buttons
3447 OKCANCEL : {ok:true, cancel:true},
3449 * Button config that displays Yes, No and Cancel buttons
3452 YESNOCANCEL : {yes:true, no:true, cancel:true},
3455 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3458 defaultTextHeight : 75,
3460 * The maximum width in pixels of the message box (defaults to 600)
3465 * The minimum width in pixels of the message box (defaults to 100)
3470 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3471 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3474 minProgressWidth : 250,
3476 * An object containing the default button text strings that can be overriden for localized language support.
3477 * Supported properties are: ok, cancel, yes and no.
3478 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3491 * Shorthand for {@link Roo.MessageBox}
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3503 * @class Roo.bootstrap.Navbar
3504 * @extends Roo.bootstrap.Component
3505 * Bootstrap Navbar class
3508 * Create a new Navbar
3509 * @param {Object} config The config object
3513 Roo.bootstrap.Navbar = function(config){
3514 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3518 * @event beforetoggle
3519 * Fire before toggle the menu
3520 * @param {Roo.EventObject} e
3522 "beforetoggle" : true
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3535 getAutoCreate : function(){
3538 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3542 initEvents :function ()
3544 //Roo.log(this.el.select('.navbar-toggle',true));
3545 this.el.select('.navbar-toggle',true).on('click', function() {
3546 if(this.fireEvent('beforetoggle', this) !== false){
3547 this.el.select('.navbar-collapse',true).toggleClass('in');
3557 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3559 var size = this.el.getSize();
3560 this.maskEl.setSize(size.width, size.height);
3561 this.maskEl.enableDisplayMode("block");
3570 getChildContainer : function()
3572 if (this.el.select('.collapse').getCount()) {
3573 return this.el.select('.collapse',true).first();
3606 * @class Roo.bootstrap.NavSimplebar
3607 * @extends Roo.bootstrap.Navbar
3608 * Bootstrap Sidebar class
3610 * @cfg {Boolean} inverse is inverted color
3612 * @cfg {String} type (nav | pills | tabs)
3613 * @cfg {Boolean} arrangement stacked | justified
3614 * @cfg {String} align (left | right) alignment
3616 * @cfg {Boolean} main (true|false) main nav bar? default false
3617 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3619 * @cfg {String} tag (header|footer|nav|div) default is nav
3625 * Create a new Sidebar
3626 * @param {Object} config The config object
3630 Roo.bootstrap.NavSimplebar = function(config){
3631 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3650 getAutoCreate : function(){
3654 tag : this.tag || 'div',
3667 this.type = this.type || 'nav';
3668 if (['tabs','pills'].indexOf(this.type)!==-1) {
3669 cfg.cn[0].cls += ' nav-' + this.type
3673 if (this.type!=='nav') {
3674 Roo.log('nav type must be nav/tabs/pills')
3676 cfg.cn[0].cls += ' navbar-nav'
3682 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683 cfg.cn[0].cls += ' nav-' + this.arrangement;
3687 if (this.align === 'right') {
3688 cfg.cn[0].cls += ' navbar-right';
3692 cfg.cls += ' navbar-inverse';
3719 * @class Roo.bootstrap.NavHeaderbar
3720 * @extends Roo.bootstrap.NavSimplebar
3721 * Bootstrap Sidebar class
3723 * @cfg {String} brand what is brand
3724 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725 * @cfg {String} brand_href href of the brand
3726 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3727 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3732 * Create a new Sidebar
3733 * @param {Object} config The config object
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3749 desktopCenter : false,
3752 getAutoCreate : function(){
3755 tag: this.nav || 'nav',
3762 if (this.desktopCenter) {
3763 cn.push({cls : 'container', cn : []});
3770 cls: 'navbar-header',
3775 cls: 'navbar-toggle',
3776 'data-toggle': 'collapse',
3781 html: 'Toggle navigation'
3803 cls: 'collapse navbar-collapse',
3807 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3809 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810 cfg.cls += ' navbar-' + this.position;
3812 // tag can override this..
3814 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3817 if (this.brand !== '') {
3820 href: this.brand_href ? this.brand_href : '#',
3821 cls: 'navbar-brand',
3829 cfg.cls += ' main-nav';
3837 getHeaderChildContainer : function()
3839 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840 return this.el.select('.navbar-header',true).first();
3843 return this.getChildContainer();
3847 initEvents : function()
3849 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3851 if (this.autohide) {
3856 Roo.get(document).on('scroll',function(e) {
3857 var ns = Roo.get(document).getScroll().top;
3858 var os = prevScroll;
3862 ft.removeClass('slideDown');
3863 ft.addClass('slideUp');
3866 ft.removeClass('slideUp');
3867 ft.addClass('slideDown');
3888 * @class Roo.bootstrap.NavSidebar
3889 * @extends Roo.bootstrap.Navbar
3890 * Bootstrap Sidebar class
3893 * Create a new Sidebar
3894 * @param {Object} config The config object
3898 Roo.bootstrap.NavSidebar = function(config){
3899 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3904 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3906 getAutoCreate : function(){
3911 cls: 'sidebar sidebar-nav'
3933 * @class Roo.bootstrap.NavGroup
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap NavGroup class
3936 * @cfg {String} align (left|right)
3937 * @cfg {Boolean} inverse
3938 * @cfg {String} type (nav|pills|tab) default nav
3939 * @cfg {String} navId - reference Id for navbar.
3943 * Create a new nav group
3944 * @param {Object} config The config object
3947 Roo.bootstrap.NavGroup = function(config){
3948 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3951 Roo.bootstrap.NavGroup.register(this);
3955 * Fires when the active item changes
3956 * @param {Roo.bootstrap.NavGroup} this
3957 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3976 getAutoCreate : function()
3978 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3985 if (['tabs','pills'].indexOf(this.type)!==-1) {
3986 cfg.cls += ' nav-' + this.type
3988 if (this.type!=='nav') {
3989 Roo.log('nav type must be nav/tabs/pills')
3991 cfg.cls += ' navbar-nav'
3994 if (this.parent().sidebar) {
3997 cls: 'dashboard-menu sidebar-menu'
4003 if (this.form === true) {
4009 if (this.align === 'right') {
4010 cfg.cls += ' navbar-right';
4012 cfg.cls += ' navbar-left';
4016 if (this.align === 'right') {
4017 cfg.cls += ' navbar-right';
4021 cfg.cls += ' navbar-inverse';
4029 * sets the active Navigation item
4030 * @param {Roo.bootstrap.NavItem} the new current navitem
4032 setActiveItem : function(item)
4035 Roo.each(this.navItems, function(v){
4040 v.setActive(false, true);
4047 item.setActive(true, true);
4048 this.fireEvent('changed', this, item, prev);
4053 * gets the active Navigation item
4054 * @return {Roo.bootstrap.NavItem} the current navitem
4056 getActive : function()
4060 Roo.each(this.navItems, function(v){
4071 indexOfNav : function()
4075 Roo.each(this.navItems, function(v,i){
4086 * adds a Navigation item
4087 * @param {Roo.bootstrap.NavItem} the navitem to add
4089 addItem : function(cfg)
4091 var cn = new Roo.bootstrap.NavItem(cfg);
4093 cn.parentId = this.id;
4094 cn.onRender(this.el, null);
4098 * register a Navigation item
4099 * @param {Roo.bootstrap.NavItem} the navitem to add
4101 register : function(item)
4103 this.navItems.push( item);
4104 item.navId = this.navId;
4109 * clear all the Navigation item
4112 clearAll : function()
4115 this.el.dom.innerHTML = '';
4118 getNavItem: function(tabId)
4121 Roo.each(this.navItems, function(e) {
4122 if (e.tabId == tabId) {
4132 setActiveNext : function()
4134 var i = this.indexOfNav(this.getActive());
4135 if (i > this.navItems.length) {
4138 this.setActiveItem(this.navItems[i+1]);
4140 setActivePrev : function()
4142 var i = this.indexOfNav(this.getActive());
4146 this.setActiveItem(this.navItems[i-1]);
4148 clearWasActive : function(except) {
4149 Roo.each(this.navItems, function(e) {
4150 if (e.tabId != except.tabId && e.was_active) {
4151 e.was_active = false;
4158 getWasActive : function ()
4161 Roo.each(this.navItems, function(e) {
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4180 * register a Navigation Group
4181 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4183 register : function(navgrp)
4185 this.groups[navgrp.navId] = navgrp;
4189 * fetch a Navigation Group based on the navigation ID
4190 * @param {string} the navgroup to add
4191 * @returns {Roo.bootstrap.NavGroup} the navgroup
4193 get: function(navId) {
4194 if (typeof(this.groups[navId]) == 'undefined') {
4196 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4198 return this.groups[navId] ;
4213 * @class Roo.bootstrap.NavItem
4214 * @extends Roo.bootstrap.Component
4215 * Bootstrap Navbar.NavItem class
4216 * @cfg {String} href link to
4217 * @cfg {String} html content of button
4218 * @cfg {String} badge text inside badge
4219 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220 * @cfg {String} glyphicon name of glyphicon
4221 * @cfg {String} icon name of font awesome icon
4222 * @cfg {Boolean} active Is item active
4223 * @cfg {Boolean} disabled Is item disabled
4225 * @cfg {Boolean} preventDefault (true | false) default false
4226 * @cfg {String} tabId the tab that this item activates.
4227 * @cfg {String} tagtype (a|span) render as a href or span?
4228 * @cfg {Boolean} animateRef (true|false) link to element default false
4231 * Create a new Navbar Item
4232 * @param {Object} config The config object
4234 Roo.bootstrap.NavItem = function(config){
4235 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4240 * The raw click event for the entire grid.
4241 * @param {Roo.EventObject} e
4246 * Fires when the active item active state changes
4247 * @param {Roo.bootstrap.NavItem} this
4248 * @param {boolean} state the new state
4254 * Fires when scroll to element
4255 * @param {Roo.bootstrap.NavItem} this
4256 * @param {Object} options
4257 * @param {Roo.EventObject} e
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4273 preventDefault : false,
4280 getAutoCreate : function(){
4289 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4291 if (this.disabled) {
4292 cfg.cls += ' disabled';
4295 if (this.href || this.html || this.glyphicon || this.icon) {
4299 href : this.href || "#",
4300 html: this.html || ''
4305 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4308 if(this.glyphicon) {
4309 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4314 cfg.cn[0].html += " <span class='caret'></span>";
4318 if (this.badge !== '') {
4320 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4328 initEvents: function()
4330 if (typeof (this.menu) != 'undefined') {
4331 this.menu.parentType = this.xtype;
4332 this.menu.triggerEl = this.el;
4333 this.menu = this.addxtype(Roo.apply({}, this.menu));
4336 this.el.select('a',true).on('click', this.onClick, this);
4338 if(this.tagtype == 'span'){
4339 this.el.select('span',true).on('click', this.onClick, this);
4342 // at this point parent should be available..
4343 this.parent().register(this);
4346 onClick : function(e)
4348 if (e.getTarget('.dropdown-menu-item')) {
4349 // did you click on a menu itemm.... - then don't trigger onclick..
4354 this.preventDefault ||
4357 Roo.log("NavItem - prevent Default?");
4361 if (this.disabled) {
4365 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366 if (tg && tg.transition) {
4367 Roo.log("waiting for the transitionend");
4373 //Roo.log("fire event clicked");
4374 if(this.fireEvent('click', this, e) === false){
4378 if(this.tagtype == 'span'){
4382 //Roo.log(this.href);
4383 var ael = this.el.select('a',true).first();
4386 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389 return; // ignore... - it's a 'hash' to another page.
4391 Roo.log("NavItem - prevent Default?");
4393 this.scrollToElement(e);
4397 var p = this.parent();
4399 if (['tabs','pills'].indexOf(p.type)!==-1) {
4400 if (typeof(p.setActiveItem) !== 'undefined') {
4401 p.setActiveItem(this);
4405 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407 // remove the collapsed menu expand...
4408 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4412 isActive: function () {
4415 setActive : function(state, fire, is_was_active)
4417 if (this.active && !state && this.navId) {
4418 this.was_active = true;
4419 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421 nv.clearWasActive(this);
4425 this.active = state;
4428 this.el.removeClass('active');
4429 } else if (!this.el.hasClass('active')) {
4430 this.el.addClass('active');
4433 this.fireEvent('changed', this, state);
4436 // show a panel if it's registered and related..
4438 if (!this.navId || !this.tabId || !state || is_was_active) {
4442 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4446 var pan = tg.getPanelByName(this.tabId);
4450 // if we can not flip to new panel - go back to old nav highlight..
4451 if (false == tg.showPanel(pan)) {
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 var onav = nv.getWasActive();
4456 onav.setActive(true, false, true);
4465 // this should not be here...
4466 setDisabled : function(state)
4468 this.disabled = state;
4470 this.el.removeClass('disabled');
4471 } else if (!this.el.hasClass('disabled')) {
4472 this.el.addClass('disabled');
4478 * Fetch the element to display the tooltip on.
4479 * @return {Roo.Element} defaults to this.el
4481 tooltipEl : function()
4483 return this.el.select('' + this.tagtype + '', true).first();
4486 scrollToElement : function(e)
4488 var c = document.body;
4491 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4493 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494 c = document.documentElement;
4497 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4503 var o = target.calcOffsetsTo(c);
4510 this.fireEvent('scrollto', this, options, e);
4512 Roo.get(c).scrollTo('top', options.value, true);
4525 * <span> icon </span>
4526 * <span> text </span>
4527 * <span>badge </span>
4531 * @class Roo.bootstrap.NavSidebarItem
4532 * @extends Roo.bootstrap.NavItem
4533 * Bootstrap Navbar.NavSidebarItem class
4534 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535 * {bool} open is the menu open
4537 * Create a new Navbar Button
4538 * @param {Object} config The config object
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4546 * The raw click event for the entire grid.
4547 * @param {Roo.EventObject} e
4552 * Fires when the active item active state changes
4553 * @param {Roo.bootstrap.NavSidebarItem} this
4554 * @param {boolean} state the new state
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4564 badgeWeight : 'default',
4568 getAutoCreate : function(){
4573 href : this.href || '#',
4585 html : this.html || ''
4590 cfg.cls += ' active';
4593 if (this.disabled) {
4594 cfg.cls += ' disabled';
4597 cfg.cls += ' open x-open';
4600 if (this.glyphicon || this.icon) {
4601 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4602 a.cn.push({ tag : 'i', cls : c }) ;
4607 if (this.badge !== '') {
4609 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4613 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614 a.cls += 'dropdown-toggle treeview' ;
4622 initEvents : function()
4624 if (typeof (this.menu) != 'undefined') {
4625 this.menu.parentType = this.xtype;
4626 this.menu.triggerEl = this.el;
4627 this.menu = this.addxtype(Roo.apply({}, this.menu));
4630 this.el.on('click', this.onClick, this);
4633 if(this.badge !== ''){
4635 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4640 onClick : function(e)
4647 if(this.preventDefault){
4651 this.fireEvent('click', this);
4654 disable : function()
4656 this.setDisabled(true);
4661 this.setDisabled(false);
4664 setDisabled : function(state)
4666 if(this.disabled == state){
4670 this.disabled = state;
4673 this.el.addClass('disabled');
4677 this.el.removeClass('disabled');
4682 setActive : function(state)
4684 if(this.active == state){
4688 this.active = state;
4691 this.el.addClass('active');
4695 this.el.removeClass('active');
4700 isActive: function ()
4705 setBadge : function(str)
4711 this.badgeEl.dom.innerHTML = str;
4728 * @class Roo.bootstrap.Row
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Row class (contains columns...)
4734 * @param {Object} config The config object
4737 Roo.bootstrap.Row = function(config){
4738 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4743 getAutoCreate : function(){
4762 * @class Roo.bootstrap.Element
4763 * @extends Roo.bootstrap.Component
4764 * Bootstrap Element class
4765 * @cfg {String} html contents of the element
4766 * @cfg {String} tag tag of the element
4767 * @cfg {String} cls class of the element
4768 * @cfg {Boolean} preventDefault (true|false) default false
4769 * @cfg {Boolean} clickable (true|false) default false
4772 * Create a new Element
4773 * @param {Object} config The config object
4776 Roo.bootstrap.Element = function(config){
4777 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4783 * When a element is chick
4784 * @param {Roo.bootstrap.Element} this
4785 * @param {Roo.EventObject} e
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4796 preventDefault: false,
4799 getAutoCreate : function(){
4810 initEvents: function()
4812 Roo.bootstrap.Element.superclass.initEvents.call(this);
4815 this.el.on('click', this.onClick, this);
4820 onClick : function(e)
4822 if(this.preventDefault){
4826 this.fireEvent('click', this, e);
4829 getValue : function()
4831 return this.el.dom.innerHTML;
4834 setValue : function(value)
4836 this.el.dom.innerHTML = value;
4851 * @class Roo.bootstrap.Pagination
4852 * @extends Roo.bootstrap.Component
4853 * Bootstrap Pagination class
4854 * @cfg {String} size xs | sm | md | lg
4855 * @cfg {Boolean} inverse false | true
4858 * Create a new Pagination
4859 * @param {Object} config The config object
4862 Roo.bootstrap.Pagination = function(config){
4863 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4872 getAutoCreate : function(){
4878 cfg.cls += ' inverse';
4884 cfg.cls += " " + this.cls;
4902 * @class Roo.bootstrap.PaginationItem
4903 * @extends Roo.bootstrap.Component
4904 * Bootstrap PaginationItem class
4905 * @cfg {String} html text
4906 * @cfg {String} href the link
4907 * @cfg {Boolean} preventDefault (true | false) default true
4908 * @cfg {Boolean} active (true | false) default false
4909 * @cfg {Boolean} disabled default false
4913 * Create a new PaginationItem
4914 * @param {Object} config The config object
4918 Roo.bootstrap.PaginationItem = function(config){
4919 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4924 * The raw click event for the entire grid.
4925 * @param {Roo.EventObject} e
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4935 preventDefault: true,
4940 getAutoCreate : function(){
4946 href : this.href ? this.href : '#',
4947 html : this.html ? this.html : ''
4957 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4961 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4967 initEvents: function() {
4969 this.el.on('click', this.onClick, this);
4972 onClick : function(e)
4974 Roo.log('PaginationItem on click ');
4975 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4999 * @class Roo.bootstrap.Slider
5000 * @extends Roo.bootstrap.Component
5001 * Bootstrap Slider class
5004 * Create a new Slider
5005 * @param {Object} config The config object
5008 Roo.bootstrap.Slider = function(config){
5009 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5014 getAutoCreate : function(){
5018 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5022 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5034 * Ext JS Library 1.1.1
5035 * Copyright(c) 2006-2007, Ext JS, LLC.
5037 * Originally Released Under LGPL - original licence link has changed is not relivant.
5040 * <script type="text/javascript">
5045 * @class Roo.grid.ColumnModel
5046 * @extends Roo.util.Observable
5047 * This is the default implementation of a ColumnModel used by the Grid. It defines
5048 * the columns in the grid.
5051 var colModel = new Roo.grid.ColumnModel([
5052 {header: "Ticker", width: 60, sortable: true, locked: true},
5053 {header: "Company Name", width: 150, sortable: true},
5054 {header: "Market Cap.", width: 100, sortable: true},
5055 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056 {header: "Employees", width: 100, sortable: true, resizable: false}
5061 * The config options listed for this class are options which may appear in each
5062 * individual column definition.
5063 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5065 * @param {Object} config An Array of column config objects. See this class's
5066 * config objects for details.
5068 Roo.grid.ColumnModel = function(config){
5070 * The config passed into the constructor
5072 this.config = config;
5075 // if no id, create one
5076 // if the column does not have a dataIndex mapping,
5077 // map it to the order it is in the config
5078 for(var i = 0, len = config.length; i < len; i++){
5080 if(typeof c.dataIndex == "undefined"){
5083 if(typeof c.renderer == "string"){
5084 c.renderer = Roo.util.Format[c.renderer];
5086 if(typeof c.id == "undefined"){
5089 if(c.editor && c.editor.xtype){
5090 c.editor = Roo.factory(c.editor, Roo.grid);
5092 if(c.editor && c.editor.isFormField){
5093 c.editor = new Roo.grid.GridEditor(c.editor);
5095 this.lookup[c.id] = c;
5099 * The width of columns which have no width specified (defaults to 100)
5102 this.defaultWidth = 100;
5105 * Default sortable of columns which have no sortable specified (defaults to false)
5108 this.defaultSortable = false;
5112 * @event widthchange
5113 * Fires when the width of a column changes.
5114 * @param {ColumnModel} this
5115 * @param {Number} columnIndex The column index
5116 * @param {Number} newWidth The new width
5118 "widthchange": true,
5120 * @event headerchange
5121 * Fires when the text of a header changes.
5122 * @param {ColumnModel} this
5123 * @param {Number} columnIndex The column index
5124 * @param {Number} newText The new header text
5126 "headerchange": true,
5128 * @event hiddenchange
5129 * Fires when a column is hidden or "unhidden".
5130 * @param {ColumnModel} this
5131 * @param {Number} columnIndex The column index
5132 * @param {Boolean} hidden true if hidden, false otherwise
5134 "hiddenchange": true,
5136 * @event columnmoved
5137 * Fires when a column is moved.
5138 * @param {ColumnModel} this
5139 * @param {Number} oldIndex
5140 * @param {Number} newIndex
5142 "columnmoved" : true,
5144 * @event columlockchange
5145 * Fires when a column's locked state is changed
5146 * @param {ColumnModel} this
5147 * @param {Number} colIndex
5148 * @param {Boolean} locked true if locked
5150 "columnlockchange" : true
5152 Roo.grid.ColumnModel.superclass.constructor.call(this);
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5156 * @cfg {String} header The header text to display in the Grid view.
5159 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161 * specified, the column's index is used as an index into the Record's data Array.
5164 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5168 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169 * Defaults to the value of the {@link #defaultSortable} property.
5170 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5173 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5176 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5179 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5182 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5185 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5191 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5194 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5197 * @cfg {String} cursor (Optional)
5200 * @cfg {String} tooltip (Optional)
5203 * @cfg {Number} xs (Optional)
5206 * @cfg {Number} sm (Optional)
5209 * @cfg {Number} md (Optional)
5212 * @cfg {Number} lg (Optional)
5215 * Returns the id of the column at the specified index.
5216 * @param {Number} index The column index
5217 * @return {String} the id
5219 getColumnId : function(index){
5220 return this.config[index].id;
5224 * Returns the column for a specified id.
5225 * @param {String} id The column id
5226 * @return {Object} the column
5228 getColumnById : function(id){
5229 return this.lookup[id];
5234 * Returns the column for a specified dataIndex.
5235 * @param {String} dataIndex The column dataIndex
5236 * @return {Object|Boolean} the column or false if not found
5238 getColumnByDataIndex: function(dataIndex){
5239 var index = this.findColumnIndex(dataIndex);
5240 return index > -1 ? this.config[index] : false;
5244 * Returns the index for a specified column id.
5245 * @param {String} id The column id
5246 * @return {Number} the index, or -1 if not found
5248 getIndexById : function(id){
5249 for(var i = 0, len = this.config.length; i < len; i++){
5250 if(this.config[i].id == id){
5258 * Returns the index for a specified column dataIndex.
5259 * @param {String} dataIndex The column dataIndex
5260 * @return {Number} the index, or -1 if not found
5263 findColumnIndex : function(dataIndex){
5264 for(var i = 0, len = this.config.length; i < len; i++){
5265 if(this.config[i].dataIndex == dataIndex){
5273 moveColumn : function(oldIndex, newIndex){
5274 var c = this.config[oldIndex];
5275 this.config.splice(oldIndex, 1);
5276 this.config.splice(newIndex, 0, c);
5277 this.dataMap = null;
5278 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5281 isLocked : function(colIndex){
5282 return this.config[colIndex].locked === true;
5285 setLocked : function(colIndex, value, suppressEvent){
5286 if(this.isLocked(colIndex) == value){
5289 this.config[colIndex].locked = value;
5291 this.fireEvent("columnlockchange", this, colIndex, value);
5295 getTotalLockedWidth : function(){
5297 for(var i = 0; i < this.config.length; i++){
5298 if(this.isLocked(i) && !this.isHidden(i)){
5299 this.totalWidth += this.getColumnWidth(i);
5305 getLockedCount : function(){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 if(!this.isLocked(i)){
5312 return this.config.length;
5316 * Returns the number of columns.
5319 getColumnCount : function(visibleOnly){
5320 if(visibleOnly === true){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(!this.isHidden(i)){
5329 return this.config.length;
5333 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334 * @param {Function} fn
5335 * @param {Object} scope (optional)
5336 * @return {Array} result
5338 getColumnsBy : function(fn, scope){
5340 for(var i = 0, len = this.config.length; i < len; i++){
5341 var c = this.config[i];
5342 if(fn.call(scope||this, c, i) === true){
5350 * Returns true if the specified column is sortable.
5351 * @param {Number} col The column index
5354 isSortable : function(col){
5355 if(typeof this.config[col].sortable == "undefined"){
5356 return this.defaultSortable;
5358 return this.config[col].sortable;
5362 * Returns the rendering (formatting) function defined for the column.
5363 * @param {Number} col The column index.
5364 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5366 getRenderer : function(col){
5367 if(!this.config[col].renderer){
5368 return Roo.grid.ColumnModel.defaultRenderer;
5370 return this.config[col].renderer;
5374 * Sets the rendering (formatting) function for a column.
5375 * @param {Number} col The column index
5376 * @param {Function} fn The function to use to process the cell's raw data
5377 * to return HTML markup for the grid view. The render function is called with
5378 * the following parameters:<ul>
5379 * <li>Data value.</li>
5380 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381 * <li>css A CSS style string to apply to the table cell.</li>
5382 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384 * <li>Row index</li>
5385 * <li>Column index</li>
5386 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5388 setRenderer : function(col, fn){
5389 this.config[col].renderer = fn;
5393 * Returns the width for the specified column.
5394 * @param {Number} col The column index
5397 getColumnWidth : function(col){
5398 return this.config[col].width * 1 || this.defaultWidth;
5402 * Sets the width for a column.
5403 * @param {Number} col The column index
5404 * @param {Number} width The new width
5406 setColumnWidth : function(col, width, suppressEvent){
5407 this.config[col].width = width;
5408 this.totalWidth = null;
5410 this.fireEvent("widthchange", this, col, width);
5415 * Returns the total width of all columns.
5416 * @param {Boolean} includeHidden True to include hidden column widths
5419 getTotalWidth : function(includeHidden){
5420 if(!this.totalWidth){
5421 this.totalWidth = 0;
5422 for(var i = 0, len = this.config.length; i < len; i++){
5423 if(includeHidden || !this.isHidden(i)){
5424 this.totalWidth += this.getColumnWidth(i);
5428 return this.totalWidth;
5432 * Returns the header for the specified column.
5433 * @param {Number} col The column index
5436 getColumnHeader : function(col){
5437 return this.config[col].header;
5441 * Sets the header for a column.
5442 * @param {Number} col The column index
5443 * @param {String} header The new header
5445 setColumnHeader : function(col, header){
5446 this.config[col].header = header;
5447 this.fireEvent("headerchange", this, col, header);
5451 * Returns the tooltip for the specified column.
5452 * @param {Number} col The column index
5455 getColumnTooltip : function(col){
5456 return this.config[col].tooltip;
5459 * Sets the tooltip for a column.
5460 * @param {Number} col The column index
5461 * @param {String} tooltip The new tooltip
5463 setColumnTooltip : function(col, tooltip){
5464 this.config[col].tooltip = tooltip;
5468 * Returns the dataIndex for the specified column.
5469 * @param {Number} col The column index
5472 getDataIndex : function(col){
5473 return this.config[col].dataIndex;
5477 * Sets the dataIndex for a column.
5478 * @param {Number} col The column index
5479 * @param {Number} dataIndex The new dataIndex
5481 setDataIndex : function(col, dataIndex){
5482 this.config[col].dataIndex = dataIndex;
5488 * Returns true if the cell is editable.
5489 * @param {Number} colIndex The column index
5490 * @param {Number} rowIndex The row index - this is nto actually used..?
5493 isCellEditable : function(colIndex, rowIndex){
5494 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5498 * Returns the editor defined for the cell/column.
5499 * return false or null to disable editing.
5500 * @param {Number} colIndex The column index
5501 * @param {Number} rowIndex The row index
5504 getCellEditor : function(colIndex, rowIndex){
5505 return this.config[colIndex].editor;
5509 * Sets if a column is editable.
5510 * @param {Number} col The column index
5511 * @param {Boolean} editable True if the column is editable
5513 setEditable : function(col, editable){
5514 this.config[col].editable = editable;
5519 * Returns true if the column is hidden.
5520 * @param {Number} colIndex The column index
5523 isHidden : function(colIndex){
5524 return this.config[colIndex].hidden;
5529 * Returns true if the column width cannot be changed
5531 isFixed : function(colIndex){
5532 return this.config[colIndex].fixed;
5536 * Returns true if the column can be resized
5539 isResizable : function(colIndex){
5540 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5543 * Sets if a column is hidden.
5544 * @param {Number} colIndex The column index
5545 * @param {Boolean} hidden True if the column is hidden
5547 setHidden : function(colIndex, hidden){
5548 this.config[colIndex].hidden = hidden;
5549 this.totalWidth = null;
5550 this.fireEvent("hiddenchange", this, colIndex, hidden);
5554 * Sets the editor for a column.
5555 * @param {Number} col The column index
5556 * @param {Object} editor The editor object
5558 setEditor : function(col, editor){
5559 this.config[col].editor = editor;
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5565 if(typeof value == "object") {
5568 if(typeof value == "string" && value.length < 1){
5572 return String.format("{0}", value);
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5579 * Ext JS Library 1.1.1
5580 * Copyright(c) 2006-2007, Ext JS, LLC.
5582 * Originally Released Under LGPL - original licence link has changed is not relivant.
5585 * <script type="text/javascript">
5589 * @class Roo.LoadMask
5590 * A simple utility class for generically masking elements while loading data. If the element being masked has
5591 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5593 * element's UpdateManager load indicator and will be destroyed after the initial load.
5595 * Create a new LoadMask
5596 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597 * @param {Object} config The config object
5599 Roo.LoadMask = function(el, config){
5600 this.el = Roo.get(el);
5601 Roo.apply(this, config);
5603 this.store.on('beforeload', this.onBeforeLoad, this);
5604 this.store.on('load', this.onLoad, this);
5605 this.store.on('loadexception', this.onLoadException, this);
5606 this.removeMask = false;
5608 var um = this.el.getUpdateManager();
5609 um.showLoadIndicator = false; // disable the default indicator
5610 um.on('beforeupdate', this.onBeforeLoad, this);
5611 um.on('update', this.onLoad, this);
5612 um.on('failure', this.onLoad, this);
5613 this.removeMask = true;
5617 Roo.LoadMask.prototype = {
5619 * @cfg {Boolean} removeMask
5620 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5625 * The text to display in a centered loading message box (defaults to 'Loading...')
5629 * @cfg {String} msgCls
5630 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5632 msgCls : 'x-mask-loading',
5635 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5641 * Disables the mask to prevent it from being displayed
5643 disable : function(){
5644 this.disabled = true;
5648 * Enables the mask so that it can be displayed
5650 enable : function(){
5651 this.disabled = false;
5654 onLoadException : function()
5658 if (typeof(arguments[3]) != 'undefined') {
5659 Roo.MessageBox.alert("Error loading",arguments[3]);
5663 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5673 this.el.unmask(this.removeMask);
5678 this.el.unmask(this.removeMask);
5682 onBeforeLoad : function(){
5684 this.el.mask(this.msg, this.msgCls);
5689 destroy : function(){
5691 this.store.un('beforeload', this.onBeforeLoad, this);
5692 this.store.un('load', this.onLoad, this);
5693 this.store.un('loadexception', this.onLoadException, this);
5695 var um = this.el.getUpdateManager();
5696 um.un('beforeupdate', this.onBeforeLoad, this);
5697 um.un('update', this.onLoad, this);
5698 um.un('failure', this.onLoad, this);
5709 * @class Roo.bootstrap.Table
5710 * @extends Roo.bootstrap.Component
5711 * Bootstrap Table class
5712 * @cfg {String} cls table class
5713 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714 * @cfg {String} bgcolor Specifies the background color for a table
5715 * @cfg {Number} border Specifies whether the table cells should have borders or not
5716 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717 * @cfg {Number} cellspacing Specifies the space between cells
5718 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720 * @cfg {String} sortable Specifies that the table should be sortable
5721 * @cfg {String} summary Specifies a summary of the content of a table
5722 * @cfg {Number} width Specifies the width of a table
5723 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5725 * @cfg {boolean} striped Should the rows be alternative striped
5726 * @cfg {boolean} bordered Add borders to the table
5727 * @cfg {boolean} hover Add hover highlighting
5728 * @cfg {boolean} condensed Format condensed
5729 * @cfg {boolean} responsive Format condensed
5730 * @cfg {Boolean} loadMask (true|false) default false
5731 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733 * @cfg {Boolean} rowSelection (true|false) default false
5734 * @cfg {Boolean} cellSelection (true|false) default false
5735 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5740 * Create a new Table
5741 * @param {Object} config The config object
5744 Roo.bootstrap.Table = function(config){
5745 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5750 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5755 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5757 this.sm.grid = this;
5758 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759 this.sm = this.selModel;
5760 this.sm.xmodule = this.xmodule || false;
5763 if (this.cm && typeof(this.cm.config) == 'undefined') {
5764 this.colModel = new Roo.grid.ColumnModel(this.cm);
5765 this.cm = this.colModel;
5766 this.cm.xmodule = this.xmodule || false;
5769 this.store= Roo.factory(this.store, Roo.data);
5770 this.ds = this.store;
5771 this.ds.xmodule = this.xmodule || false;
5774 if (this.footer && this.store) {
5775 this.footer.dataSource = this.ds;
5776 this.footer = Roo.factory(this.footer);
5783 * Fires when a cell is clicked
5784 * @param {Roo.bootstrap.Table} this
5785 * @param {Roo.Element} el
5786 * @param {Number} rowIndex
5787 * @param {Number} columnIndex
5788 * @param {Roo.EventObject} e
5792 * @event celldblclick
5793 * Fires when a cell is double clicked
5794 * @param {Roo.bootstrap.Table} this
5795 * @param {Roo.Element} el
5796 * @param {Number} rowIndex
5797 * @param {Number} columnIndex
5798 * @param {Roo.EventObject} e
5800 "celldblclick" : true,
5803 * Fires when a row is clicked
5804 * @param {Roo.bootstrap.Table} this
5805 * @param {Roo.Element} el
5806 * @param {Number} rowIndex
5807 * @param {Roo.EventObject} e
5811 * @event rowdblclick
5812 * Fires when a row is double clicked
5813 * @param {Roo.bootstrap.Table} this
5814 * @param {Roo.Element} el
5815 * @param {Number} rowIndex
5816 * @param {Roo.EventObject} e
5818 "rowdblclick" : true,
5821 * Fires when a mouseover occur
5822 * @param {Roo.bootstrap.Table} this
5823 * @param {Roo.Element} el
5824 * @param {Number} rowIndex
5825 * @param {Number} columnIndex
5826 * @param {Roo.EventObject} e
5831 * Fires when a mouseout occur
5832 * @param {Roo.bootstrap.Table} this
5833 * @param {Roo.Element} el
5834 * @param {Number} rowIndex
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5841 * Fires when a row is rendered, so you can change add a style to it.
5842 * @param {Roo.bootstrap.Table} this
5843 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5847 * @event rowsrendered
5848 * Fires when all the rows have been rendered
5849 * @param {Roo.bootstrap.Table} this
5851 'rowsrendered' : true,
5853 * @event contextmenu
5854 * The raw contextmenu event for the entire grid.
5855 * @param {Roo.EventObject} e
5857 "contextmenu" : true,
5859 * @event rowcontextmenu
5860 * Fires when a row is right clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Number} rowIndex
5863 * @param {Roo.EventObject} e
5865 "rowcontextmenu" : true,
5867 * @event cellcontextmenu
5868 * Fires when a cell is right clicked
5869 * @param {Roo.bootstrap.Table} this
5870 * @param {Number} rowIndex
5871 * @param {Number} cellIndex
5872 * @param {Roo.EventObject} e
5874 "cellcontextmenu" : true,
5876 * @event headercontextmenu
5877 * Fires when a header is right clicked
5878 * @param {Roo.bootstrap.Table} this
5879 * @param {Number} columnIndex
5880 * @param {Roo.EventObject} e
5882 "headercontextmenu" : true
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5912 rowSelection : false,
5913 cellSelection : false,
5916 // Roo.Element - the tbody
5918 // Roo.Element - thead element
5921 container: false, // used by gridpanel...
5923 getAutoCreate : function()
5925 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5932 if (this.scrollBody) {
5933 cfg.cls += ' table-body-fixed';
5936 cfg.cls += ' table-striped';
5940 cfg.cls += ' table-hover';
5942 if (this.bordered) {
5943 cfg.cls += ' table-bordered';
5945 if (this.condensed) {
5946 cfg.cls += ' table-condensed';
5948 if (this.responsive) {
5949 cfg.cls += ' table-responsive';
5953 cfg.cls+= ' ' +this.cls;
5956 // this lot should be simplifed...
5959 cfg.align=this.align;
5962 cfg.bgcolor=this.bgcolor;
5965 cfg.border=this.border;
5967 if (this.cellpadding) {
5968 cfg.cellpadding=this.cellpadding;
5970 if (this.cellspacing) {
5971 cfg.cellspacing=this.cellspacing;
5974 cfg.frame=this.frame;
5977 cfg.rules=this.rules;
5979 if (this.sortable) {
5980 cfg.sortable=this.sortable;
5983 cfg.summary=this.summary;
5986 cfg.width=this.width;
5989 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5992 if(this.store || this.cm){
5993 if(this.headerShow){
5994 cfg.cn.push(this.renderHeader());
5997 cfg.cn.push(this.renderBody());
5999 if(this.footerShow){
6000 cfg.cn.push(this.renderFooter());
6002 // where does this come from?
6003 //cfg.cls+= ' TableGrid';
6006 return { cn : [ cfg ] };
6009 initEvents : function()
6011 if(!this.store || !this.cm){
6014 if (this.selModel) {
6015 this.selModel.initEvents();
6019 //Roo.log('initEvents with ds!!!!');
6021 this.mainBody = this.el.select('tbody', true).first();
6022 this.mainHead = this.el.select('thead', true).first();
6029 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030 e.on('click', _this.sort, _this);
6033 this.mainBody.on("click", this.onClick, this);
6034 this.mainBody.on("dblclick", this.onDblClick, this);
6036 // why is this done????? = it breaks dialogs??
6037 //this.parent().el.setStyle('position', 'relative');
6041 this.footer.parentId = this.id;
6042 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6045 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6047 this.store.on('load', this.onLoad, this);
6048 this.store.on('beforeload', this.onBeforeLoad, this);
6049 this.store.on('update', this.onUpdate, this);
6050 this.store.on('add', this.onAdd, this);
6051 this.store.on("clear", this.clear, this);
6053 this.el.on("contextmenu", this.onContextMenu, this);
6055 this.mainBody.on('scroll', this.onBodyScroll, this);
6060 onContextMenu : function(e, t)
6062 this.processEvent("contextmenu", e);
6065 processEvent : function(name, e)
6067 if (name != 'touchstart' ) {
6068 this.fireEvent(name, e);
6071 var t = e.getTarget();
6073 var cell = Roo.get(t);
6079 if(cell.findParent('tfoot', false, true)){
6083 if(cell.findParent('thead', false, true)){
6085 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086 cell = Roo.get(t).findParent('th', false, true);
6088 Roo.log("failed to find th in thead?");
6089 Roo.log(e.getTarget());
6094 var cellIndex = cell.dom.cellIndex;
6096 var ename = name == 'touchstart' ? 'click' : name;
6097 this.fireEvent("header" + ename, this, cellIndex, e);
6102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103 cell = Roo.get(t).findParent('td', false, true);
6105 Roo.log("failed to find th in tbody?");
6106 Roo.log(e.getTarget());
6111 var row = cell.findParent('tr', false, true);
6112 var cellIndex = cell.dom.cellIndex;
6113 var rowIndex = row.dom.rowIndex - 1;
6117 this.fireEvent("row" + name, this, rowIndex, e);
6121 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6127 onMouseover : function(e, el)
6129 var cell = Roo.get(el);
6135 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136 cell = cell.findParent('td', false, true);
6139 var row = cell.findParent('tr', false, true);
6140 var cellIndex = cell.dom.cellIndex;
6141 var rowIndex = row.dom.rowIndex - 1; // start from 0
6143 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6147 onMouseout : function(e, el)
6149 var cell = Roo.get(el);
6155 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156 cell = cell.findParent('td', false, true);
6159 var row = cell.findParent('tr', false, true);
6160 var cellIndex = cell.dom.cellIndex;
6161 var rowIndex = row.dom.rowIndex - 1; // start from 0
6163 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6167 onClick : function(e, el)
6169 var cell = Roo.get(el);
6171 if(!cell || (!this.cellSelection && !this.rowSelection)){
6175 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176 cell = cell.findParent('td', false, true);
6179 if(!cell || typeof(cell) == 'undefined'){
6183 var row = cell.findParent('tr', false, true);
6185 if(!row || typeof(row) == 'undefined'){
6189 var cellIndex = cell.dom.cellIndex;
6190 var rowIndex = this.getRowIndex(row);
6192 // why??? - should these not be based on SelectionModel?
6193 if(this.cellSelection){
6194 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6197 if(this.rowSelection){
6198 this.fireEvent('rowclick', this, row, rowIndex, e);
6204 onDblClick : function(e,el)
6206 var cell = Roo.get(el);
6208 if(!cell || (!this.cellSelection && !this.rowSelection)){
6212 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213 cell = cell.findParent('td', false, true);
6216 if(!cell || typeof(cell) == 'undefined'){
6220 var row = cell.findParent('tr', false, true);
6222 if(!row || typeof(row) == 'undefined'){
6226 var cellIndex = cell.dom.cellIndex;
6227 var rowIndex = this.getRowIndex(row);
6229 if(this.cellSelection){
6230 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6233 if(this.rowSelection){
6234 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6238 sort : function(e,el)
6240 var col = Roo.get(el);
6242 if(!col.hasClass('sortable')){
6246 var sort = col.attr('sort');
6249 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6253 this.store.sortInfo = {field : sort, direction : dir};
6256 Roo.log("calling footer first");
6257 this.footer.onClick('first');
6260 this.store.load({ params : { start : 0 } });
6264 renderHeader : function()
6272 this.totalWidth = 0;
6274 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6276 var config = cm.config[i];
6281 html: cm.getColumnHeader(i)
6286 if(typeof(config.sortable) != 'undefined' && config.sortable){
6288 c.html = '<i class="glyphicon"></i>' + c.html;
6291 if(typeof(config.lgHeader) != 'undefined'){
6292 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6295 if(typeof(config.mdHeader) != 'undefined'){
6296 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6299 if(typeof(config.smHeader) != 'undefined'){
6300 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6303 if(typeof(config.xsHeader) != 'undefined'){
6304 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6311 if(typeof(config.tooltip) != 'undefined'){
6312 c.tooltip = config.tooltip;
6315 if(typeof(config.colspan) != 'undefined'){
6316 c.colspan = config.colspan;
6319 if(typeof(config.hidden) != 'undefined' && config.hidden){
6320 c.style += ' display:none;';
6323 if(typeof(config.dataIndex) != 'undefined'){
6324 c.sort = config.dataIndex;
6329 if(typeof(config.align) != 'undefined' && config.align.length){
6330 c.style += ' text-align:' + config.align + ';';
6333 if(typeof(config.width) != 'undefined'){
6334 c.style += ' width:' + config.width + 'px;';
6335 this.totalWidth += config.width;
6337 this.totalWidth += 100; // assume minimum of 100 per column?
6340 if(typeof(config.cls) != 'undefined'){
6341 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6344 ['xs','sm','md','lg'].map(function(size){
6346 if(typeof(config[size]) == 'undefined'){
6350 if (!config[size]) { // 0 = hidden
6351 c.cls += ' hidden-' + size;
6355 c.cls += ' col-' + size + '-' + config[size];
6365 renderBody : function()
6375 colspan : this.cm.getColumnCount()
6385 renderFooter : function()
6395 colspan : this.cm.getColumnCount()
6409 // Roo.log('ds onload');
6414 var ds = this.store;
6416 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418 if (_this.store.sortInfo) {
6420 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421 e.select('i', true).addClass(['glyphicon-arrow-up']);
6424 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425 e.select('i', true).addClass(['glyphicon-arrow-down']);
6430 var tbody = this.mainBody;
6432 if(ds.getCount() > 0){
6433 ds.data.each(function(d,rowIndex){
6434 var row = this.renderRow(cm, ds, rowIndex);
6436 tbody.createChild(row);
6440 if(row.cellObjects.length){
6441 Roo.each(row.cellObjects, function(r){
6442 _this.renderCellObject(r);
6449 Roo.each(this.el.select('tbody td', true).elements, function(e){
6450 e.on('mouseover', _this.onMouseover, _this);
6453 Roo.each(this.el.select('tbody td', true).elements, function(e){
6454 e.on('mouseout', _this.onMouseout, _this);
6456 this.fireEvent('rowsrendered', this);
6457 //if(this.loadMask){
6458 // this.maskEl.hide();
6465 onUpdate : function(ds,record)
6467 this.refreshRow(record);
6471 onRemove : function(ds, record, index, isUpdate){
6472 if(isUpdate !== true){
6473 this.fireEvent("beforerowremoved", this, index, record);
6475 var bt = this.mainBody.dom;
6477 var rows = this.el.select('tbody > tr', true).elements;
6479 if(typeof(rows[index]) != 'undefined'){
6480 bt.removeChild(rows[index].dom);
6483 // if(bt.rows[index]){
6484 // bt.removeChild(bt.rows[index]);
6487 if(isUpdate !== true){
6488 //this.stripeRows(index);
6489 //this.syncRowHeights(index, index);
6491 this.fireEvent("rowremoved", this, index, record);
6495 onAdd : function(ds, records, rowIndex)
6497 //Roo.log('on Add called');
6498 // - note this does not handle multiple adding very well..
6499 var bt = this.mainBody.dom;
6500 for (var i =0 ; i < records.length;i++) {
6501 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502 //Roo.log(records[i]);
6503 //Roo.log(this.store.getAt(rowIndex+i));
6504 this.insertRow(this.store, rowIndex + i, false);
6511 refreshRow : function(record){
6512 var ds = this.store, index;
6513 if(typeof record == 'number'){
6515 record = ds.getAt(index);
6517 index = ds.indexOf(record);
6519 this.insertRow(ds, index, true);
6521 this.onRemove(ds, record, index+1, true);
6523 //this.syncRowHeights(index, index);
6525 this.fireEvent("rowupdated", this, index, record);
6528 insertRow : function(dm, rowIndex, isUpdate){
6531 this.fireEvent("beforerowsinserted", this, rowIndex);
6533 //var s = this.getScrollState();
6534 var row = this.renderRow(this.cm, this.store, rowIndex);
6535 // insert before rowIndex..
6536 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6540 if(row.cellObjects.length){
6541 Roo.each(row.cellObjects, function(r){
6542 _this.renderCellObject(r);
6547 this.fireEvent("rowsinserted", this, rowIndex);
6548 //this.syncRowHeights(firstRow, lastRow);
6549 //this.stripeRows(firstRow);
6556 getRowDom : function(rowIndex)
6558 var rows = this.el.select('tbody > tr', true).elements;
6560 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6563 // returns the object tree for a tr..
6566 renderRow : function(cm, ds, rowIndex)
6569 var d = ds.getAt(rowIndex);
6576 var cellObjects = [];
6578 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579 var config = cm.config[i];
6581 var renderer = cm.getRenderer(i);
6585 if(typeof(renderer) !== 'undefined'){
6586 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6588 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589 // and are rendered into the cells after the row is rendered - using the id for the element.
6591 if(typeof(value) === 'object'){
6601 rowIndex : rowIndex,
6606 this.fireEvent('rowclass', this, rowcfg);
6610 cls : rowcfg.rowClass,
6612 html: (typeof(value) === 'object') ? '' : value
6619 if(typeof(config.colspan) != 'undefined'){
6620 td.colspan = config.colspan;
6623 if(typeof(config.hidden) != 'undefined' && config.hidden){
6624 td.style += ' display:none;';
6627 if(typeof(config.align) != 'undefined' && config.align.length){
6628 td.style += ' text-align:' + config.align + ';';
6631 if(typeof(config.width) != 'undefined'){
6632 td.style += ' width:' + config.width + 'px;';
6635 if(typeof(config.cursor) != 'undefined'){
6636 td.style += ' cursor:' + config.cursor + ';';
6639 if(typeof(config.cls) != 'undefined'){
6640 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6643 ['xs','sm','md','lg'].map(function(size){
6645 if(typeof(config[size]) == 'undefined'){
6649 if (!config[size]) { // 0 = hidden
6650 td.cls += ' hidden-' + size;
6654 td.cls += ' col-' + size + '-' + config[size];
6662 row.cellObjects = cellObjects;
6670 onBeforeLoad : function()
6672 //Roo.log('ds onBeforeLoad');
6676 //if(this.loadMask){
6677 // this.maskEl.show();
6685 this.el.select('tbody', true).first().dom.innerHTML = '';
6688 * Show or hide a row.
6689 * @param {Number} rowIndex to show or hide
6690 * @param {Boolean} state hide
6692 setRowVisibility : function(rowIndex, state)
6694 var bt = this.mainBody.dom;
6696 var rows = this.el.select('tbody > tr', true).elements;
6698 if(typeof(rows[rowIndex]) == 'undefined'){
6701 rows[rowIndex].dom.style.display = state ? '' : 'none';
6705 getSelectionModel : function(){
6707 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6709 return this.selModel;
6712 * Render the Roo.bootstrap object from renderder
6714 renderCellObject : function(r)
6718 var t = r.cfg.render(r.container);
6721 Roo.each(r.cfg.cn, function(c){
6723 container: t.getChildContainer(),
6726 _this.renderCellObject(child);
6731 getRowIndex : function(row)
6735 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6746 * Returns the grid's underlying element = used by panel.Grid
6747 * @return {Element} The element
6749 getGridEl : function(){
6753 * Forces a resize - used by panel.Grid
6754 * @return {Element} The element
6756 autoSize : function()
6758 //var ctr = Roo.get(this.container.dom.parentElement);
6759 var ctr = Roo.get(this.el.dom);
6761 var thd = this.getGridEl().select('thead',true).first();
6762 var tbd = this.getGridEl().select('tbody', true).first();
6763 var tfd = this.getGridEl().select('tfoot', true).first();
6765 var cw = ctr.getWidth();
6769 tbd.setSize(ctr.getWidth(),
6770 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6772 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6775 cw = Math.max(cw, this.totalWidth);
6776 this.getGridEl().select('tr',true).setWidth(cw);
6777 // resize 'expandable coloumn?
6779 return; // we doe not have a view in this design..
6782 onBodyScroll: function()
6785 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786 this.mainHead.setStyle({
6787 'position' : 'relative',
6788 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6805 * @class Roo.bootstrap.TableCell
6806 * @extends Roo.bootstrap.Component
6807 * Bootstrap TableCell class
6808 * @cfg {String} html cell contain text
6809 * @cfg {String} cls cell class
6810 * @cfg {String} tag cell tag (td|th) default td
6811 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812 * @cfg {String} align Aligns the content in a cell
6813 * @cfg {String} axis Categorizes cells
6814 * @cfg {String} bgcolor Specifies the background color of a cell
6815 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816 * @cfg {Number} colspan Specifies the number of columns a cell should span
6817 * @cfg {String} headers Specifies one or more header cells a cell is related to
6818 * @cfg {Number} height Sets the height of a cell
6819 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820 * @cfg {Number} rowspan Sets the number of rows a cell should span
6821 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822 * @cfg {String} valign Vertical aligns the content in a cell
6823 * @cfg {Number} width Specifies the width of a cell
6826 * Create a new TableCell
6827 * @param {Object} config The config object
6830 Roo.bootstrap.TableCell = function(config){
6831 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6854 getAutoCreate : function(){
6855 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6875 cfg.align=this.align
6881 cfg.bgcolor=this.bgcolor
6884 cfg.charoff=this.charoff
6887 cfg.colspan=this.colspan
6890 cfg.headers=this.headers
6893 cfg.height=this.height
6896 cfg.nowrap=this.nowrap
6899 cfg.rowspan=this.rowspan
6902 cfg.scope=this.scope
6905 cfg.valign=this.valign
6908 cfg.width=this.width
6927 * @class Roo.bootstrap.TableRow
6928 * @extends Roo.bootstrap.Component
6929 * Bootstrap TableRow class
6930 * @cfg {String} cls row class
6931 * @cfg {String} align Aligns the content in a table row
6932 * @cfg {String} bgcolor Specifies a background color for a table row
6933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934 * @cfg {String} valign Vertical aligns the content in a table row
6937 * Create a new TableRow
6938 * @param {Object} config The config object
6941 Roo.bootstrap.TableRow = function(config){
6942 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6953 getAutoCreate : function(){
6954 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6964 cfg.align = this.align;
6967 cfg.bgcolor = this.bgcolor;
6970 cfg.charoff = this.charoff;
6973 cfg.valign = this.valign;
6991 * @class Roo.bootstrap.TableBody
6992 * @extends Roo.bootstrap.Component
6993 * Bootstrap TableBody class
6994 * @cfg {String} cls element class
6995 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996 * @cfg {String} align Aligns the content inside the element
6997 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7001 * Create a new TableBody
7002 * @param {Object} config The config object
7005 Roo.bootstrap.TableBody = function(config){
7006 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7017 getAutoCreate : function(){
7018 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7032 cfg.align = this.align;
7035 cfg.charoff = this.charoff;
7038 cfg.valign = this.valign;
7045 // initEvents : function()
7052 // this.store = Roo.factory(this.store, Roo.data);
7053 // this.store.on('load', this.onLoad, this);
7055 // this.store.load();
7059 // onLoad: function ()
7061 // this.fireEvent('load', this);
7071 * Ext JS Library 1.1.1
7072 * Copyright(c) 2006-2007, Ext JS, LLC.
7074 * Originally Released Under LGPL - original licence link has changed is not relivant.
7077 * <script type="text/javascript">
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7083 * @class Roo.form.Action
7084 * Internal Class used to handle form actions
7086 * @param {Roo.form.BasicForm} el The form element or its id
7087 * @param {Object} config Configuration options
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7095 this.options = options || {};
7098 * Client Validation Failed
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7103 * Server Validation Failed
7106 Roo.form.Action.SERVER_INVALID = 'server';
7108 * Connect to Server Failed
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7113 * Reading Data from Server Failed
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7118 Roo.form.Action.prototype = {
7120 failureType : undefined,
7121 response : undefined,
7125 run : function(options){
7130 success : function(response){
7135 handleResponse : function(response){
7139 // default connection failure
7140 failure : function(response){
7142 this.response = response;
7143 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144 this.form.afterAction(this, false);
7147 processResponse : function(response){
7148 this.response = response;
7149 if(!response.responseText){
7152 this.result = this.handleResponse(response);
7156 // utility functions used internally
7157 getUrl : function(appendParams){
7158 var url = this.options.url || this.form.url || this.form.el.dom.action;
7160 var p = this.getParams();
7162 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7168 getMethod : function(){
7169 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7172 getParams : function(){
7173 var bp = this.form.baseParams;
7174 var p = this.options.params;
7176 if(typeof p == "object"){
7177 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178 }else if(typeof p == 'string' && bp){
7179 p += '&' + Roo.urlEncode(bp);
7182 p = Roo.urlEncode(bp);
7187 createCallback : function(){
7189 success: this.success,
7190 failure: this.failure,
7192 timeout: (this.form.timeout*1000),
7193 upload: this.form.fileUpload ? this.success : undefined
7198 Roo.form.Action.Submit = function(form, options){
7199 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7205 haveProgress : false,
7206 uploadComplete : false,
7208 // uploadProgress indicator.
7209 uploadProgress : function()
7211 if (!this.form.progressUrl) {
7215 if (!this.haveProgress) {
7216 Roo.MessageBox.progress("Uploading", "Uploading");
7218 if (this.uploadComplete) {
7219 Roo.MessageBox.hide();
7223 this.haveProgress = true;
7225 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7227 var c = new Roo.data.Connection();
7229 url : this.form.progressUrl,
7234 success : function(req){
7235 //console.log(data);
7239 rdata = Roo.decode(req.responseText)
7241 Roo.log("Invalid data from server..");
7245 if (!rdata || !rdata.success) {
7247 Roo.MessageBox.alert(Roo.encode(rdata));
7250 var data = rdata.data;
7252 if (this.uploadComplete) {
7253 Roo.MessageBox.hide();
7258 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7262 this.uploadProgress.defer(2000,this);
7265 failure: function(data) {
7266 Roo.log('progress url failed ');
7277 // run get Values on the form, so it syncs any secondary forms.
7278 this.form.getValues();
7280 var o = this.options;
7281 var method = this.getMethod();
7282 var isPost = method == 'POST';
7283 if(o.clientValidation === false || this.form.isValid()){
7285 if (this.form.progressUrl) {
7286 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287 (new Date() * 1) + '' + Math.random());
7292 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293 form:this.form.el.dom,
7294 url:this.getUrl(!isPost),
7296 params:isPost ? this.getParams() : null,
7297 isUpload: this.form.fileUpload
7300 this.uploadProgress();
7302 }else if (o.clientValidation !== false){ // client validation failed
7303 this.failureType = Roo.form.Action.CLIENT_INVALID;
7304 this.form.afterAction(this, false);
7308 success : function(response)
7310 this.uploadComplete= true;
7311 if (this.haveProgress) {
7312 Roo.MessageBox.hide();
7316 var result = this.processResponse(response);
7317 if(result === true || result.success){
7318 this.form.afterAction(this, true);
7322 this.form.markInvalid(result.errors);
7323 this.failureType = Roo.form.Action.SERVER_INVALID;
7325 this.form.afterAction(this, false);
7327 failure : function(response)
7329 this.uploadComplete= true;
7330 if (this.haveProgress) {
7331 Roo.MessageBox.hide();
7334 this.response = response;
7335 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336 this.form.afterAction(this, false);
7339 handleResponse : function(response){
7340 if(this.form.errorReader){
7341 var rs = this.form.errorReader.read(response);
7344 for(var i = 0, len = rs.records.length; i < len; i++) {
7345 var r = rs.records[i];
7349 if(errors.length < 1){
7353 success : rs.success,
7359 ret = Roo.decode(response.responseText);
7363 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7373 Roo.form.Action.Load = function(form, options){
7374 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375 this.reader = this.form.reader;
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7383 Roo.Ajax.request(Roo.apply(
7384 this.createCallback(), {
7385 method:this.getMethod(),
7386 url:this.getUrl(false),
7387 params:this.getParams()
7391 success : function(response){
7393 var result = this.processResponse(response);
7394 if(result === true || !result.success || !result.data){
7395 this.failureType = Roo.form.Action.LOAD_FAILURE;
7396 this.form.afterAction(this, false);
7399 this.form.clearInvalid();
7400 this.form.setValues(result.data);
7401 this.form.afterAction(this, true);
7404 handleResponse : function(response){
7405 if(this.form.reader){
7406 var rs = this.form.reader.read(response);
7407 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7409 success : rs.success,
7413 return Roo.decode(response.responseText);
7417 Roo.form.Action.ACTION_TYPES = {
7418 'load' : Roo.form.Action.Load,
7419 'submit' : Roo.form.Action.Submit
7428 * @class Roo.bootstrap.Form
7429 * @extends Roo.bootstrap.Component
7430 * Bootstrap Form class
7431 * @cfg {String} method GET | POST (default POST)
7432 * @cfg {String} labelAlign top | left (default top)
7433 * @cfg {String} align left | right - for navbars
7434 * @cfg {Boolean} loadMask load mask when submit (default true)
7439 * @param {Object} config The config object
7443 Roo.bootstrap.Form = function(config){
7444 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7447 * @event clientvalidation
7448 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449 * @param {Form} this
7450 * @param {Boolean} valid true if the form has passed client-side validation
7452 clientvalidation: true,
7454 * @event beforeaction
7455 * Fires before any action is performed. Return false to cancel the action.
7456 * @param {Form} this
7457 * @param {Action} action The action to be performed
7461 * @event actionfailed
7462 * Fires when an action fails.
7463 * @param {Form} this
7464 * @param {Action} action The action that failed
7466 actionfailed : true,
7468 * @event actioncomplete
7469 * Fires when an action is completed.
7470 * @param {Form} this
7471 * @param {Action} action The action that completed
7473 actioncomplete : true
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7481 * @cfg {String} method
7482 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7487 * The URL to use for form actions if one isn't supplied in the action options.
7490 * @cfg {Boolean} fileUpload
7491 * Set to true if this form is a file upload.
7495 * @cfg {Object} baseParams
7496 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504 * @cfg {Sting} align (left|right) for navbar forms
7509 activeAction : null,
7512 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513 * element by passing it or its id or mask the form itself by passing in true.
7516 waitMsgTarget : false,
7521 * @cfg {Boolean} errPopover (true|false) default false
7525 getAutoCreate : function(){
7529 method : this.method || 'POST',
7530 id : this.id || Roo.id(),
7533 if (this.parent().xtype.match(/^Nav/)) {
7534 cfg.cls = 'navbar-form navbar-' + this.align;
7538 if (this.labelAlign == 'left' ) {
7539 cfg.cls += ' form-horizontal';
7545 initEvents : function()
7547 this.el.on('submit', this.onSubmit, this);
7548 // this was added as random key presses on the form where triggering form submit.
7549 this.el.on('keypress', function(e) {
7550 if (e.getCharCode() != 13) {
7553 // we might need to allow it for textareas.. and some other items.
7554 // check e.getTarget().
7556 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560 Roo.log("keypress blocked");
7568 onSubmit : function(e){
7573 * Returns true if client-side validation on the form is successful.
7576 isValid : function(){
7577 var items = this.getItems();
7582 items.each(function(f){
7596 if(this.errPopover && !valid){
7597 this.showErrPopover(target);
7603 showErrPopover : function(target)
7605 if(!this.errPopover){
7609 target.inputEl().focus();
7611 var oIndex = target.el.getStyle('z-index');
7613 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7615 target.el.addClass('roo-invalid-outline');
7617 var fadeout = function(){
7619 target.inputEl().un('blur', fadeout);
7620 target.inputEl().un('keyup', fadeout);
7622 target.el.setStyle('z-index', oIndex);
7624 target.el.removeClass('roo-invalid-outline');
7628 target.inputEl().on('blur', fadeout);
7629 target.inputEl().on('keyup', fadeout);
7638 * Returns true if any fields in this form have changed since their original load.
7641 isDirty : function(){
7643 var items = this.getItems();
7644 items.each(function(f){
7654 * Performs a predefined action (submit or load) or custom actions you define on this form.
7655 * @param {String} actionName The name of the action type
7656 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7657 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7658 * accept other config options):
7660 Property Type Description
7661 ---------------- --------------- ----------------------------------------------------------------------------------
7662 url String The url for the action (defaults to the form's url)
7663 method String The form method to use (defaults to the form's method, or POST if not defined)
7664 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7665 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7666 validate the form on the client (defaults to false)
7668 * @return {BasicForm} this
7670 doAction : function(action, options){
7671 if(typeof action == 'string'){
7672 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7674 if(this.fireEvent('beforeaction', this, action) !== false){
7675 this.beforeAction(action);
7676 action.run.defer(100, action);
7682 beforeAction : function(action){
7683 var o = action.options;
7686 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7688 // not really supported yet.. ??
7690 //if(this.waitMsgTarget === true){
7691 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7692 //}else if(this.waitMsgTarget){
7693 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7694 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7696 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7702 afterAction : function(action, success){
7703 this.activeAction = null;
7704 var o = action.options;
7706 //if(this.waitMsgTarget === true){
7708 //}else if(this.waitMsgTarget){
7709 // this.waitMsgTarget.unmask();
7711 // Roo.MessageBox.updateProgress(1);
7712 // Roo.MessageBox.hide();
7719 Roo.callback(o.success, o.scope, [this, action]);
7720 this.fireEvent('actioncomplete', this, action);
7724 // failure condition..
7725 // we have a scenario where updates need confirming.
7726 // eg. if a locking scenario exists..
7727 // we look for { errors : { needs_confirm : true }} in the response.
7729 (typeof(action.result) != 'undefined') &&
7730 (typeof(action.result.errors) != 'undefined') &&
7731 (typeof(action.result.errors.needs_confirm) != 'undefined')
7734 Roo.log("not supported yet");
7737 Roo.MessageBox.confirm(
7738 "Change requires confirmation",
7739 action.result.errorMsg,
7744 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7754 Roo.callback(o.failure, o.scope, [this, action]);
7755 // show an error message if no failed handler is set..
7756 if (!this.hasListener('actionfailed')) {
7757 Roo.log("need to add dialog support");
7759 Roo.MessageBox.alert("Error",
7760 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7761 action.result.errorMsg :
7762 "Saving Failed, please check your entries or try again"
7767 this.fireEvent('actionfailed', this, action);
7772 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7773 * @param {String} id The value to search for
7776 findField : function(id){
7777 var items = this.getItems();
7778 var field = items.get(id);
7780 items.each(function(f){
7781 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7788 return field || null;
7791 * Mark fields in this form invalid in bulk.
7792 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7793 * @return {BasicForm} this
7795 markInvalid : function(errors){
7796 if(errors instanceof Array){
7797 for(var i = 0, len = errors.length; i < len; i++){
7798 var fieldError = errors[i];
7799 var f = this.findField(fieldError.id);
7801 f.markInvalid(fieldError.msg);
7807 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7808 field.markInvalid(errors[id]);
7812 //Roo.each(this.childForms || [], function (f) {
7813 // f.markInvalid(errors);
7820 * Set values for fields in this form in bulk.
7821 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7822 * @return {BasicForm} this
7824 setValues : function(values){
7825 if(values instanceof Array){ // array of objects
7826 for(var i = 0, len = values.length; i < len; i++){
7828 var f = this.findField(v.id);
7830 f.setValue(v.value);
7831 if(this.trackResetOnLoad){
7832 f.originalValue = f.getValue();
7836 }else{ // object hash
7839 if(typeof values[id] != 'function' && (field = this.findField(id))){
7841 if (field.setFromData &&
7843 field.displayField &&
7844 // combos' with local stores can
7845 // be queried via setValue()
7846 // to set their value..
7847 (field.store && !field.store.isLocal)
7851 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7852 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7853 field.setFromData(sd);
7856 field.setValue(values[id]);
7860 if(this.trackResetOnLoad){
7861 field.originalValue = field.getValue();
7867 //Roo.each(this.childForms || [], function (f) {
7868 // f.setValues(values);
7875 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7876 * they are returned as an array.
7877 * @param {Boolean} asString
7880 getValues : function(asString){
7881 //if (this.childForms) {
7882 // copy values from the child forms
7883 // Roo.each(this.childForms, function (f) {
7884 // this.setValues(f.getValues());
7890 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7891 if(asString === true){
7894 return Roo.urlDecode(fs);
7898 * Returns the fields in this form as an object with key/value pairs.
7899 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7902 getFieldValues : function(with_hidden)
7904 var items = this.getItems();
7906 items.each(function(f){
7910 var v = f.getValue();
7911 if (f.inputType =='radio') {
7912 if (typeof(ret[f.getName()]) == 'undefined') {
7913 ret[f.getName()] = ''; // empty..
7916 if (!f.el.dom.checked) {
7924 // not sure if this supported any more..
7925 if ((typeof(v) == 'object') && f.getRawValue) {
7926 v = f.getRawValue() ; // dates..
7928 // combo boxes where name != hiddenName...
7929 if (f.name != f.getName()) {
7930 ret[f.name] = f.getRawValue();
7932 ret[f.getName()] = v;
7939 * Clears all invalid messages in this form.
7940 * @return {BasicForm} this
7942 clearInvalid : function(){
7943 var items = this.getItems();
7945 items.each(function(f){
7956 * @return {BasicForm} this
7959 var items = this.getItems();
7960 items.each(function(f){
7964 Roo.each(this.childForms || [], function (f) {
7971 getItems : function()
7973 var r=new Roo.util.MixedCollection(false, function(o){
7974 return o.id || (o.id = Roo.id());
7976 var iter = function(el) {
7983 Roo.each(el.items,function(e) {
8001 * Ext JS Library 1.1.1
8002 * Copyright(c) 2006-2007, Ext JS, LLC.
8004 * Originally Released Under LGPL - original licence link has changed is not relivant.
8007 * <script type="text/javascript">
8010 * @class Roo.form.VTypes
8011 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8014 Roo.form.VTypes = function(){
8015 // closure these in so they are only created once.
8016 var alpha = /^[a-zA-Z_]+$/;
8017 var alphanum = /^[a-zA-Z0-9_]+$/;
8018 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8019 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8021 // All these messages and functions are configurable
8024 * The function used to validate email addresses
8025 * @param {String} value The email address
8027 'email' : function(v){
8028 return email.test(v);
8031 * The error text to display when the email validation function returns false
8034 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8036 * The keystroke filter mask to be applied on email input
8039 'emailMask' : /[a-z0-9_\.\-@]/i,
8042 * The function used to validate URLs
8043 * @param {String} value The URL
8045 'url' : function(v){
8049 * The error text to display when the url validation function returns false
8052 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8055 * The function used to validate alpha values
8056 * @param {String} value The value
8058 'alpha' : function(v){
8059 return alpha.test(v);
8062 * The error text to display when the alpha validation function returns false
8065 'alphaText' : 'This field should only contain letters and _',
8067 * The keystroke filter mask to be applied on alpha input
8070 'alphaMask' : /[a-z_]/i,
8073 * The function used to validate alphanumeric values
8074 * @param {String} value The value
8076 'alphanum' : function(v){
8077 return alphanum.test(v);
8080 * The error text to display when the alphanumeric validation function returns false
8083 'alphanumText' : 'This field should only contain letters, numbers and _',
8085 * The keystroke filter mask to be applied on alphanumeric input
8088 'alphanumMask' : /[a-z0-9_]/i
8098 * @class Roo.bootstrap.Input
8099 * @extends Roo.bootstrap.Component
8100 * Bootstrap Input class
8101 * @cfg {Boolean} disabled is it disabled
8102 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8103 * @cfg {String} name name of the input
8104 * @cfg {string} fieldLabel - the label associated
8105 * @cfg {string} placeholder - placeholder to put in text.
8106 * @cfg {string} before - input group add on before
8107 * @cfg {string} after - input group add on after
8108 * @cfg {string} size - (lg|sm) or leave empty..
8109 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8110 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8111 * @cfg {Number} md colspan out of 12 for computer-sized screens
8112 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8113 * @cfg {string} value default value of the input
8114 * @cfg {Number} labelWidth set the width of label (0-12)
8115 * @cfg {String} labelAlign (top|left)
8116 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8117 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8118 * @cfg {String} indicatorpos (left|right) default left
8120 * @cfg {String} align (left|center|right) Default left
8121 * @cfg {Boolean} forceFeedback (true|false) Default false
8127 * Create a new Input
8128 * @param {Object} config The config object
8131 Roo.bootstrap.Input = function(config){
8132 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8137 * Fires when this field receives input focus.
8138 * @param {Roo.form.Field} this
8143 * Fires when this field loses input focus.
8144 * @param {Roo.form.Field} this
8149 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8150 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8151 * @param {Roo.form.Field} this
8152 * @param {Roo.EventObject} e The event object
8157 * Fires just before the field blurs if the field value has changed.
8158 * @param {Roo.form.Field} this
8159 * @param {Mixed} newValue The new value
8160 * @param {Mixed} oldValue The original value
8165 * Fires after the field has been marked as invalid.
8166 * @param {Roo.form.Field} this
8167 * @param {String} msg The validation message
8172 * Fires after the field has been validated with no errors.
8173 * @param {Roo.form.Field} this
8178 * Fires after the key up
8179 * @param {Roo.form.Field} this
8180 * @param {Roo.EventObject} e The event Object
8186 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8188 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8189 automatic validation (defaults to "keyup").
8191 validationEvent : "keyup",
8193 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8195 validateOnBlur : true,
8197 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8199 validationDelay : 250,
8201 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8203 focusClass : "x-form-focus", // not needed???
8207 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8209 invalidClass : "has-warning",
8212 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8214 validClass : "has-success",
8217 * @cfg {Boolean} hasFeedback (true|false) default true
8222 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8224 invalidFeedbackClass : "glyphicon-warning-sign",
8227 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8229 validFeedbackClass : "glyphicon-ok",
8232 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8234 selectOnFocus : false,
8237 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8241 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8246 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8248 disableKeyFilter : false,
8251 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8255 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8259 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8261 blankText : "This field is required",
8264 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8268 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8270 maxLength : Number.MAX_VALUE,
8272 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8274 minLengthText : "The minimum length for this field is {0}",
8276 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8278 maxLengthText : "The maximum length for this field is {0}",
8282 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8283 * If available, this function will be called only after the basic validators all return true, and will be passed the
8284 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8288 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8289 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8290 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8294 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8298 autocomplete: false,
8317 formatedValue : false,
8318 forceFeedback : false,
8320 indicatorpos : 'left',
8322 parentLabelAlign : function()
8325 while (parent.parent()) {
8326 parent = parent.parent();
8327 if (typeof(parent.labelAlign) !='undefined') {
8328 return parent.labelAlign;
8335 getAutoCreate : function()
8337 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8343 if(this.inputType != 'hidden'){
8344 cfg.cls = 'form-group' //input-group
8350 type : this.inputType,
8352 cls : 'form-control',
8353 placeholder : this.placeholder || '',
8354 autocomplete : this.autocomplete || 'new-password'
8358 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8361 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8362 input.maxLength = this.maxLength;
8365 if (this.disabled) {
8366 input.disabled=true;
8369 if (this.readOnly) {
8370 input.readonly=true;
8374 input.name = this.name;
8378 input.cls += ' input-' + this.size;
8382 ['xs','sm','md','lg'].map(function(size){
8383 if (settings[size]) {
8384 cfg.cls += ' col-' + size + '-' + settings[size];
8388 var inputblock = input;
8392 cls: 'glyphicon form-control-feedback'
8395 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8398 cls : 'has-feedback',
8406 if (this.before || this.after) {
8409 cls : 'input-group',
8413 if (this.before && typeof(this.before) == 'string') {
8415 inputblock.cn.push({
8417 cls : 'roo-input-before input-group-addon',
8421 if (this.before && typeof(this.before) == 'object') {
8422 this.before = Roo.factory(this.before);
8424 inputblock.cn.push({
8426 cls : 'roo-input-before input-group-' +
8427 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8431 inputblock.cn.push(input);
8433 if (this.after && typeof(this.after) == 'string') {
8434 inputblock.cn.push({
8436 cls : 'roo-input-after input-group-addon',
8440 if (this.after && typeof(this.after) == 'object') {
8441 this.after = Roo.factory(this.after);
8443 inputblock.cn.push({
8445 cls : 'roo-input-after input-group-' +
8446 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8450 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8451 inputblock.cls += ' has-feedback';
8452 inputblock.cn.push(feedback);
8456 if (align ==='left' && this.fieldLabel.length) {
8461 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8462 tooltip : 'This field is required'
8467 cls : 'control-label col-sm-' + this.labelWidth,
8468 html : this.fieldLabel
8472 cls : "col-sm-" + (12 - this.labelWidth),
8480 if(this.indicatorpos == 'right'){
8485 cls : 'control-label col-sm-' + this.labelWidth,
8486 html : this.fieldLabel
8491 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8492 tooltip : 'This field is required'
8495 cls : "col-sm-" + (12 - this.labelWidth),
8504 } else if ( this.fieldLabel.length) {
8509 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8510 tooltip : 'This field is required'
8514 //cls : 'input-group-addon',
8515 html : this.fieldLabel
8523 if(this.indicatorpos == 'right'){
8528 //cls : 'input-group-addon',
8529 html : this.fieldLabel
8534 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8535 tooltip : 'This field is required'
8555 if (this.parentType === 'Navbar' && this.parent().bar) {
8556 cfg.cls += ' navbar-form';
8559 if (this.parentType === 'NavGroup') {
8560 cfg.cls += ' navbar-form';
8568 * return the real input element.
8570 inputEl: function ()
8572 return this.el.select('input.form-control',true).first();
8575 tooltipEl : function()
8577 return this.inputEl();
8580 indicatorEl : function()
8582 var indicator = this.el.select('i.roo-required-indicator',true).first();
8592 setDisabled : function(v)
8594 var i = this.inputEl().dom;
8596 i.removeAttribute('disabled');
8600 i.setAttribute('disabled','true');
8602 initEvents : function()
8605 this.inputEl().on("keydown" , this.fireKey, this);
8606 this.inputEl().on("focus", this.onFocus, this);
8607 this.inputEl().on("blur", this.onBlur, this);
8609 this.inputEl().relayEvent('keyup', this);
8611 this.indicator = this.indicatorEl();
8614 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8615 this.indicator.hide();
8618 // reference to original value for reset
8619 this.originalValue = this.getValue();
8620 //Roo.form.TextField.superclass.initEvents.call(this);
8621 if(this.validationEvent == 'keyup'){
8622 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8623 this.inputEl().on('keyup', this.filterValidation, this);
8625 else if(this.validationEvent !== false){
8626 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8629 if(this.selectOnFocus){
8630 this.on("focus", this.preFocus, this);
8633 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8634 this.inputEl().on("keypress", this.filterKeys, this);
8636 this.inputEl().relayEvent('keypress', this);
8639 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8640 this.el.on("click", this.autoSize, this);
8643 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8644 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8647 if (typeof(this.before) == 'object') {
8648 this.before.render(this.el.select('.roo-input-before',true).first());
8650 if (typeof(this.after) == 'object') {
8651 this.after.render(this.el.select('.roo-input-after',true).first());
8656 filterValidation : function(e){
8657 if(!e.isNavKeyPress()){
8658 this.validationTask.delay(this.validationDelay);
8662 * Validates the field value
8663 * @return {Boolean} True if the value is valid, else false
8665 validate : function(){
8666 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8667 if(this.disabled || this.validateValue(this.getRawValue())){
8678 * Validates a value according to the field's validation rules and marks the field as invalid
8679 * if the validation fails
8680 * @param {Mixed} value The value to validate
8681 * @return {Boolean} True if the value is valid, else false
8683 validateValue : function(value){
8684 if(value.length < 1) { // if it's blank
8685 if(this.allowBlank){
8691 if(value.length < this.minLength){
8694 if(value.length > this.maxLength){
8698 var vt = Roo.form.VTypes;
8699 if(!vt[this.vtype](value, this)){
8703 if(typeof this.validator == "function"){
8704 var msg = this.validator(value);
8710 if(this.regex && !this.regex.test(value)){
8720 fireKey : function(e){
8721 //Roo.log('field ' + e.getKey());
8722 if(e.isNavKeyPress()){
8723 this.fireEvent("specialkey", this, e);
8726 focus : function (selectText){
8728 this.inputEl().focus();
8729 if(selectText === true){
8730 this.inputEl().dom.select();
8736 onFocus : function(){
8737 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8738 // this.el.addClass(this.focusClass);
8741 this.hasFocus = true;
8742 this.startValue = this.getValue();
8743 this.fireEvent("focus", this);
8747 beforeBlur : Roo.emptyFn,
8751 onBlur : function(){
8753 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8754 //this.el.removeClass(this.focusClass);
8756 this.hasFocus = false;
8757 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8760 var v = this.getValue();
8761 if(String(v) !== String(this.startValue)){
8762 this.fireEvent('change', this, v, this.startValue);
8764 this.fireEvent("blur", this);
8768 * Resets the current field value to the originally loaded value and clears any validation messages
8771 this.setValue(this.originalValue);
8775 * Returns the name of the field
8776 * @return {Mixed} name The name field
8778 getName: function(){
8782 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8783 * @return {Mixed} value The field value
8785 getValue : function(){
8787 var v = this.inputEl().getValue();
8792 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8793 * @return {Mixed} value The field value
8795 getRawValue : function(){
8796 var v = this.inputEl().getValue();
8802 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8803 * @param {Mixed} value The value to set
8805 setRawValue : function(v){
8806 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8809 selectText : function(start, end){
8810 var v = this.getRawValue();
8812 start = start === undefined ? 0 : start;
8813 end = end === undefined ? v.length : end;
8814 var d = this.inputEl().dom;
8815 if(d.setSelectionRange){
8816 d.setSelectionRange(start, end);
8817 }else if(d.createTextRange){
8818 var range = d.createTextRange();
8819 range.moveStart("character", start);
8820 range.moveEnd("character", v.length-end);
8827 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8828 * @param {Mixed} value The value to set
8830 setValue : function(v){
8833 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8839 processValue : function(value){
8840 if(this.stripCharsRe){
8841 var newValue = value.replace(this.stripCharsRe, '');
8842 if(newValue !== value){
8843 this.setRawValue(newValue);
8850 preFocus : function(){
8852 if(this.selectOnFocus){
8853 this.inputEl().dom.select();
8856 filterKeys : function(e){
8858 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8861 var c = e.getCharCode(), cc = String.fromCharCode(c);
8862 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8865 if(!this.maskRe.test(cc)){
8870 * Clear any invalid styles/messages for this field
8872 clearInvalid : function(){
8874 if(!this.el || this.preventMark){ // not rendered
8879 this.indicator.hide();
8882 this.el.removeClass(this.invalidClass);
8884 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8886 var feedback = this.el.select('.form-control-feedback', true).first();
8889 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8894 this.fireEvent('valid', this);
8898 * Mark this field as valid
8900 markValid : function()
8902 if(!this.el || this.preventMark){ // not rendered
8906 this.el.removeClass([this.invalidClass, this.validClass]);
8908 var feedback = this.el.select('.form-control-feedback', true).first();
8911 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8918 if(this.allowBlank && !this.getRawValue().length){
8923 this.indicator.hide();
8926 this.el.addClass(this.validClass);
8928 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8930 var feedback = this.el.select('.form-control-feedback', true).first();
8933 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8934 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8939 this.fireEvent('valid', this);
8943 * Mark this field as invalid
8944 * @param {String} msg The validation message
8946 markInvalid : function(msg)
8948 if(!this.el || this.preventMark){ // not rendered
8952 this.el.removeClass([this.invalidClass, this.validClass]);
8954 var feedback = this.el.select('.form-control-feedback', true).first();
8957 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8964 if(this.allowBlank && !this.getRawValue().length){
8969 this.indicator.show();
8972 this.el.addClass(this.invalidClass);
8974 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8976 var feedback = this.el.select('.form-control-feedback', true).first();
8979 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8981 if(this.getValue().length || this.forceFeedback){
8982 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8989 this.fireEvent('invalid', this, msg);
8992 SafariOnKeyDown : function(event)
8994 // this is a workaround for a password hang bug on chrome/ webkit.
8995 if (this.inputEl().dom.type != 'password') {
8999 var isSelectAll = false;
9001 if(this.inputEl().dom.selectionEnd > 0){
9002 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9004 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9005 event.preventDefault();
9010 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9012 event.preventDefault();
9013 // this is very hacky as keydown always get's upper case.
9015 var cc = String.fromCharCode(event.getCharCode());
9016 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9020 adjustWidth : function(tag, w){
9021 tag = tag.toLowerCase();
9022 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9023 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9027 if(tag == 'textarea'){
9030 }else if(Roo.isOpera){
9034 if(tag == 'textarea'){
9053 * @class Roo.bootstrap.TextArea
9054 * @extends Roo.bootstrap.Input
9055 * Bootstrap TextArea class
9056 * @cfg {Number} cols Specifies the visible width of a text area
9057 * @cfg {Number} rows Specifies the visible number of lines in a text area
9058 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9059 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9060 * @cfg {string} html text
9063 * Create a new TextArea
9064 * @param {Object} config The config object
9067 Roo.bootstrap.TextArea = function(config){
9068 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9072 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9082 getAutoCreate : function(){
9084 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9095 value : this.value || '',
9096 html: this.html || '',
9097 cls : 'form-control',
9098 placeholder : this.placeholder || ''
9102 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9103 input.maxLength = this.maxLength;
9107 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9111 input.cols = this.cols;
9114 if (this.readOnly) {
9115 input.readonly = true;
9119 input.name = this.name;
9123 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9127 ['xs','sm','md','lg'].map(function(size){
9128 if (settings[size]) {
9129 cfg.cls += ' col-' + size + '-' + settings[size];
9133 var inputblock = input;
9135 if(this.hasFeedback && !this.allowBlank){
9139 cls: 'glyphicon form-control-feedback'
9143 cls : 'has-feedback',
9152 if (this.before || this.after) {
9155 cls : 'input-group',
9159 inputblock.cn.push({
9161 cls : 'input-group-addon',
9166 inputblock.cn.push(input);
9168 if(this.hasFeedback && !this.allowBlank){
9169 inputblock.cls += ' has-feedback';
9170 inputblock.cn.push(feedback);
9174 inputblock.cn.push({
9176 cls : 'input-group-addon',
9183 if (align ==='left' && this.fieldLabel.length) {
9184 // Roo.log("left and has label");
9190 cls : 'control-label col-sm-' + this.labelWidth,
9191 html : this.fieldLabel
9195 cls : "col-sm-" + (12 - this.labelWidth),
9202 } else if ( this.fieldLabel.length) {
9203 // Roo.log(" label");
9208 //cls : 'input-group-addon',
9209 html : this.fieldLabel
9219 // Roo.log(" no label && no align");
9229 if (this.disabled) {
9230 input.disabled=true;
9237 * return the real textarea element.
9239 inputEl: function ()
9241 return this.el.select('textarea.form-control',true).first();
9245 * Clear any invalid styles/messages for this field
9247 clearInvalid : function()
9250 if(!this.el || this.preventMark){ // not rendered
9254 var label = this.el.select('label', true).first();
9255 var icon = this.el.select('i.fa-star', true).first();
9261 this.el.removeClass(this.invalidClass);
9263 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9265 var feedback = this.el.select('.form-control-feedback', true).first();
9268 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9273 this.fireEvent('valid', this);
9277 * Mark this field as valid
9279 markValid : function()
9281 if(!this.el || this.preventMark){ // not rendered
9285 this.el.removeClass([this.invalidClass, this.validClass]);
9287 var feedback = this.el.select('.form-control-feedback', true).first();
9290 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9293 if(this.disabled || this.allowBlank){
9297 var label = this.el.select('label', true).first();
9298 var icon = this.el.select('i.fa-star', true).first();
9304 this.el.addClass(this.validClass);
9306 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9308 var feedback = this.el.select('.form-control-feedback', true).first();
9311 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9312 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9317 this.fireEvent('valid', this);
9321 * Mark this field as invalid
9322 * @param {String} msg The validation message
9324 markInvalid : function(msg)
9326 if(!this.el || this.preventMark){ // not rendered
9330 this.el.removeClass([this.invalidClass, this.validClass]);
9332 var feedback = this.el.select('.form-control-feedback', true).first();
9335 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9338 if(this.disabled || this.allowBlank){
9342 var label = this.el.select('label', true).first();
9343 var icon = this.el.select('i.fa-star', true).first();
9345 if(!this.getValue().length && label && !icon){
9346 this.el.createChild({
9348 cls : 'text-danger fa fa-lg fa-star',
9349 tooltip : 'This field is required',
9350 style : 'margin-right:5px;'
9354 this.el.addClass(this.invalidClass);
9356 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9358 var feedback = this.el.select('.form-control-feedback', true).first();
9361 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9363 if(this.getValue().length || this.forceFeedback){
9364 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9371 this.fireEvent('invalid', this, msg);
9379 * trigger field - base class for combo..
9384 * @class Roo.bootstrap.TriggerField
9385 * @extends Roo.bootstrap.Input
9386 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9387 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9388 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9389 * for which you can provide a custom implementation. For example:
9391 var trigger = new Roo.bootstrap.TriggerField();
9392 trigger.onTriggerClick = myTriggerFn;
9393 trigger.applyTo('my-field');
9396 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9397 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9398 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9399 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9400 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9403 * Create a new TriggerField.
9404 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9405 * to the base TextField)
9407 Roo.bootstrap.TriggerField = function(config){
9408 this.mimicing = false;
9409 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9412 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9414 * @cfg {String} triggerClass A CSS class to apply to the trigger
9417 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9422 * @cfg {Boolean} removable (true|false) special filter default false
9426 /** @cfg {Boolean} grow @hide */
9427 /** @cfg {Number} growMin @hide */
9428 /** @cfg {Number} growMax @hide */
9434 autoSize: Roo.emptyFn,
9441 actionMode : 'wrap',
9446 getAutoCreate : function(){
9448 var align = this.labelAlign || this.parentLabelAlign();
9453 cls: 'form-group' //input-group
9460 type : this.inputType,
9461 cls : 'form-control',
9462 autocomplete: 'new-password',
9463 placeholder : this.placeholder || ''
9467 input.name = this.name;
9470 input.cls += ' input-' + this.size;
9473 if (this.disabled) {
9474 input.disabled=true;
9477 var inputblock = input;
9479 if(this.hasFeedback && !this.allowBlank){
9483 cls: 'glyphicon form-control-feedback'
9486 if(this.removable && !this.editable && !this.tickable){
9488 cls : 'has-feedback',
9494 cls : 'roo-combo-removable-btn close'
9501 cls : 'has-feedback',
9510 if(this.removable && !this.editable && !this.tickable){
9512 cls : 'roo-removable',
9518 cls : 'roo-combo-removable-btn close'
9525 if (this.before || this.after) {
9528 cls : 'input-group',
9532 inputblock.cn.push({
9534 cls : 'input-group-addon',
9539 inputblock.cn.push(input);
9541 if(this.hasFeedback && !this.allowBlank){
9542 inputblock.cls += ' has-feedback';
9543 inputblock.cn.push(feedback);
9547 inputblock.cn.push({
9549 cls : 'input-group-addon',
9562 cls: 'form-hidden-field'
9576 cls: 'form-hidden-field'
9580 cls: 'roo-select2-choices',
9584 cls: 'roo-select2-search-field',
9597 cls: 'roo-select2-container input-group',
9602 // cls: 'typeahead typeahead-long dropdown-menu',
9603 // style: 'display:none'
9608 if(!this.multiple && this.showToggleBtn){
9614 if (this.caret != false) {
9617 cls: 'fa fa-' + this.caret
9624 cls : 'input-group-addon btn dropdown-toggle',
9629 cls: 'combobox-clear',
9643 combobox.cls += ' roo-select2-container-multi';
9646 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9648 // Roo.log("left and has label");
9652 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9653 tooltip : 'This field is required'
9658 cls : 'control-label col-sm-' + this.labelWidth,
9659 html : this.fieldLabel
9663 cls : "col-sm-" + (12 - this.labelWidth),
9671 if(this.indicatorpos == 'right'){
9676 cls : 'control-label col-sm-' + this.labelWidth,
9677 html : this.fieldLabel
9682 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9683 tooltip : 'This field is required'
9686 cls : "col-sm-" + (12 - this.labelWidth),
9695 } else if ( this.fieldLabel.length) {
9696 // Roo.log(" label");
9700 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9701 tooltip : 'This field is required'
9705 //cls : 'input-group-addon',
9706 html : this.fieldLabel
9714 if(this.indicatorpos == 'right'){
9719 //cls : 'input-group-addon',
9720 html : this.fieldLabel
9725 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9726 tooltip : 'This field is required'
9737 // Roo.log(" no label && no align");
9744 ['xs','sm','md','lg'].map(function(size){
9745 if (settings[size]) {
9746 cfg.cls += ' col-' + size + '-' + settings[size];
9757 onResize : function(w, h){
9758 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9759 // if(typeof w == 'number'){
9760 // var x = w - this.trigger.getWidth();
9761 // this.inputEl().setWidth(this.adjustWidth('input', x));
9762 // this.trigger.setStyle('left', x+'px');
9767 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9770 getResizeEl : function(){
9771 return this.inputEl();
9775 getPositionEl : function(){
9776 return this.inputEl();
9780 alignErrorIcon : function(){
9781 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9785 initEvents : function(){
9789 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9790 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9791 if(!this.multiple && this.showToggleBtn){
9792 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9793 if(this.hideTrigger){
9794 this.trigger.setDisplayed(false);
9796 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9800 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9803 if(this.removable && !this.editable && !this.tickable){
9804 var close = this.closeTriggerEl();
9807 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9808 close.on('click', this.removeBtnClick, this, close);
9812 //this.trigger.addClassOnOver('x-form-trigger-over');
9813 //this.trigger.addClassOnClick('x-form-trigger-click');
9816 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9820 closeTriggerEl : function()
9822 var close = this.el.select('.roo-combo-removable-btn', true).first();
9823 return close ? close : false;
9826 removeBtnClick : function(e, h, el)
9830 if(this.fireEvent("remove", this) !== false){
9832 this.fireEvent("afterremove", this)
9836 createList : function()
9838 this.list = Roo.get(document.body).createChild({
9840 cls: 'typeahead typeahead-long dropdown-menu',
9841 style: 'display:none'
9844 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9849 initTrigger : function(){
9854 onDestroy : function(){
9856 this.trigger.removeAllListeners();
9857 // this.trigger.remove();
9860 // this.wrap.remove();
9862 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9866 onFocus : function(){
9867 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9870 this.wrap.addClass('x-trigger-wrap-focus');
9871 this.mimicing = true;
9872 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9873 if(this.monitorTab){
9874 this.el.on("keydown", this.checkTab, this);
9881 checkTab : function(e){
9882 if(e.getKey() == e.TAB){
9888 onBlur : function(){
9893 mimicBlur : function(e, t){
9895 if(!this.wrap.contains(t) && this.validateBlur()){
9902 triggerBlur : function(){
9903 this.mimicing = false;
9904 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9905 if(this.monitorTab){
9906 this.el.un("keydown", this.checkTab, this);
9908 //this.wrap.removeClass('x-trigger-wrap-focus');
9909 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9913 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9914 validateBlur : function(e, t){
9919 onDisable : function(){
9920 this.inputEl().dom.disabled = true;
9921 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9923 // this.wrap.addClass('x-item-disabled');
9928 onEnable : function(){
9929 this.inputEl().dom.disabled = false;
9930 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9932 // this.el.removeClass('x-item-disabled');
9937 onShow : function(){
9938 var ae = this.getActionEl();
9941 ae.dom.style.display = '';
9942 ae.dom.style.visibility = 'visible';
9948 onHide : function(){
9949 var ae = this.getActionEl();
9950 ae.dom.style.display = 'none';
9954 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9955 * by an implementing function.
9957 * @param {EventObject} e
9959 onTriggerClick : Roo.emptyFn
9963 * Ext JS Library 1.1.1
9964 * Copyright(c) 2006-2007, Ext JS, LLC.
9966 * Originally Released Under LGPL - original licence link has changed is not relivant.
9969 * <script type="text/javascript">
9974 * @class Roo.data.SortTypes
9976 * Defines the default sorting (casting?) comparison functions used when sorting data.
9978 Roo.data.SortTypes = {
9980 * Default sort that does nothing
9981 * @param {Mixed} s The value being converted
9982 * @return {Mixed} The comparison value
9989 * The regular expression used to strip tags
9993 stripTagsRE : /<\/?[^>]+>/gi,
9996 * Strips all HTML tags to sort on text only
9997 * @param {Mixed} s The value being converted
9998 * @return {String} The comparison value
10000 asText : function(s){
10001 return String(s).replace(this.stripTagsRE, "");
10005 * Strips all HTML tags to sort on text only - Case insensitive
10006 * @param {Mixed} s The value being converted
10007 * @return {String} The comparison value
10009 asUCText : function(s){
10010 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10014 * Case insensitive string
10015 * @param {Mixed} s The value being converted
10016 * @return {String} The comparison value
10018 asUCString : function(s) {
10019 return String(s).toUpperCase();
10024 * @param {Mixed} s The value being converted
10025 * @return {Number} The comparison value
10027 asDate : function(s) {
10031 if(s instanceof Date){
10032 return s.getTime();
10034 return Date.parse(String(s));
10039 * @param {Mixed} s The value being converted
10040 * @return {Float} The comparison value
10042 asFloat : function(s) {
10043 var val = parseFloat(String(s).replace(/,/g, ""));
10052 * @param {Mixed} s The value being converted
10053 * @return {Number} The comparison value
10055 asInt : function(s) {
10056 var val = parseInt(String(s).replace(/,/g, ""));
10064 * Ext JS Library 1.1.1
10065 * Copyright(c) 2006-2007, Ext JS, LLC.
10067 * Originally Released Under LGPL - original licence link has changed is not relivant.
10070 * <script type="text/javascript">
10074 * @class Roo.data.Record
10075 * Instances of this class encapsulate both record <em>definition</em> information, and record
10076 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10077 * to access Records cached in an {@link Roo.data.Store} object.<br>
10079 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10080 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10083 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10085 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10086 * {@link #create}. The parameters are the same.
10087 * @param {Array} data An associative Array of data values keyed by the field name.
10088 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10089 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10090 * not specified an integer id is generated.
10092 Roo.data.Record = function(data, id){
10093 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10098 * Generate a constructor for a specific record layout.
10099 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10100 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10101 * Each field definition object may contain the following properties: <ul>
10102 * <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,
10103 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10104 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10105 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10106 * is being used, then this is a string containing the javascript expression to reference the data relative to
10107 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10108 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10109 * this may be omitted.</p></li>
10110 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10111 * <ul><li>auto (Default, implies no conversion)</li>
10116 * <li>date</li></ul></p></li>
10117 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10118 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10119 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10120 * by the Reader into an object that will be stored in the Record. It is passed the
10121 * following parameters:<ul>
10122 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10124 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10126 * <br>usage:<br><pre><code>
10127 var TopicRecord = Roo.data.Record.create(
10128 {name: 'title', mapping: 'topic_title'},
10129 {name: 'author', mapping: 'username'},
10130 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10131 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10132 {name: 'lastPoster', mapping: 'user2'},
10133 {name: 'excerpt', mapping: 'post_text'}
10136 var myNewRecord = new TopicRecord({
10137 title: 'Do my job please',
10140 lastPost: new Date(),
10141 lastPoster: 'Animal',
10142 excerpt: 'No way dude!'
10144 myStore.add(myNewRecord);
10149 Roo.data.Record.create = function(o){
10150 var f = function(){
10151 f.superclass.constructor.apply(this, arguments);
10153 Roo.extend(f, Roo.data.Record);
10154 var p = f.prototype;
10155 p.fields = new Roo.util.MixedCollection(false, function(field){
10158 for(var i = 0, len = o.length; i < len; i++){
10159 p.fields.add(new Roo.data.Field(o[i]));
10161 f.getField = function(name){
10162 return p.fields.get(name);
10167 Roo.data.Record.AUTO_ID = 1000;
10168 Roo.data.Record.EDIT = 'edit';
10169 Roo.data.Record.REJECT = 'reject';
10170 Roo.data.Record.COMMIT = 'commit';
10172 Roo.data.Record.prototype = {
10174 * Readonly flag - true if this record has been modified.
10183 join : function(store){
10184 this.store = store;
10188 * Set the named field to the specified value.
10189 * @param {String} name The name of the field to set.
10190 * @param {Object} value The value to set the field to.
10192 set : function(name, value){
10193 if(this.data[name] == value){
10197 if(!this.modified){
10198 this.modified = {};
10200 if(typeof this.modified[name] == 'undefined'){
10201 this.modified[name] = this.data[name];
10203 this.data[name] = value;
10204 if(!this.editing && this.store){
10205 this.store.afterEdit(this);
10210 * Get the value of the named field.
10211 * @param {String} name The name of the field to get the value of.
10212 * @return {Object} The value of the field.
10214 get : function(name){
10215 return this.data[name];
10219 beginEdit : function(){
10220 this.editing = true;
10221 this.modified = {};
10225 cancelEdit : function(){
10226 this.editing = false;
10227 delete this.modified;
10231 endEdit : function(){
10232 this.editing = false;
10233 if(this.dirty && this.store){
10234 this.store.afterEdit(this);
10239 * Usually called by the {@link Roo.data.Store} which owns the Record.
10240 * Rejects all changes made to the Record since either creation, or the last commit operation.
10241 * Modified fields are reverted to their original values.
10243 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10244 * of reject operations.
10246 reject : function(){
10247 var m = this.modified;
10249 if(typeof m[n] != "function"){
10250 this.data[n] = m[n];
10253 this.dirty = false;
10254 delete this.modified;
10255 this.editing = false;
10257 this.store.afterReject(this);
10262 * Usually called by the {@link Roo.data.Store} which owns the Record.
10263 * Commits all changes made to the Record since either creation, or the last commit operation.
10265 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10266 * of commit operations.
10268 commit : function(){
10269 this.dirty = false;
10270 delete this.modified;
10271 this.editing = false;
10273 this.store.afterCommit(this);
10278 hasError : function(){
10279 return this.error != null;
10283 clearError : function(){
10288 * Creates a copy of this record.
10289 * @param {String} id (optional) A new record id if you don't want to use this record's id
10292 copy : function(newId) {
10293 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10297 * Ext JS Library 1.1.1
10298 * Copyright(c) 2006-2007, Ext JS, LLC.
10300 * Originally Released Under LGPL - original licence link has changed is not relivant.
10303 * <script type="text/javascript">
10309 * @class Roo.data.Store
10310 * @extends Roo.util.Observable
10311 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10312 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10314 * 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
10315 * has no knowledge of the format of the data returned by the Proxy.<br>
10317 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10318 * instances from the data object. These records are cached and made available through accessor functions.
10320 * Creates a new Store.
10321 * @param {Object} config A config object containing the objects needed for the Store to access data,
10322 * and read the data into Records.
10324 Roo.data.Store = function(config){
10325 this.data = new Roo.util.MixedCollection(false);
10326 this.data.getKey = function(o){
10329 this.baseParams = {};
10331 this.paramNames = {
10336 "multisort" : "_multisort"
10339 if(config && config.data){
10340 this.inlineData = config.data;
10341 delete config.data;
10344 Roo.apply(this, config);
10346 if(this.reader){ // reader passed
10347 this.reader = Roo.factory(this.reader, Roo.data);
10348 this.reader.xmodule = this.xmodule || false;
10349 if(!this.recordType){
10350 this.recordType = this.reader.recordType;
10352 if(this.reader.onMetaChange){
10353 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10357 if(this.recordType){
10358 this.fields = this.recordType.prototype.fields;
10360 this.modified = [];
10364 * @event datachanged
10365 * Fires when the data cache has changed, and a widget which is using this Store
10366 * as a Record cache should refresh its view.
10367 * @param {Store} this
10369 datachanged : true,
10371 * @event metachange
10372 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10373 * @param {Store} this
10374 * @param {Object} meta The JSON metadata
10379 * Fires when Records have been added to the Store
10380 * @param {Store} this
10381 * @param {Roo.data.Record[]} records The array of Records added
10382 * @param {Number} index The index at which the record(s) were added
10387 * Fires when a Record has been removed from the Store
10388 * @param {Store} this
10389 * @param {Roo.data.Record} record The Record that was removed
10390 * @param {Number} index The index at which the record was removed
10395 * Fires when a Record has been updated
10396 * @param {Store} this
10397 * @param {Roo.data.Record} record The Record that was updated
10398 * @param {String} operation The update operation being performed. Value may be one of:
10400 Roo.data.Record.EDIT
10401 Roo.data.Record.REJECT
10402 Roo.data.Record.COMMIT
10408 * Fires when the data cache has been cleared.
10409 * @param {Store} this
10413 * @event beforeload
10414 * Fires before a request is made for a new data object. If the beforeload handler returns false
10415 * the load action will be canceled.
10416 * @param {Store} this
10417 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10421 * @event beforeloadadd
10422 * Fires after a new set of Records has been loaded.
10423 * @param {Store} this
10424 * @param {Roo.data.Record[]} records The Records that were loaded
10425 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10427 beforeloadadd : true,
10430 * Fires after a new set of Records has been loaded, before they are added to the store.
10431 * @param {Store} this
10432 * @param {Roo.data.Record[]} records The Records that were loaded
10433 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10434 * @params {Object} return from reader
10438 * @event loadexception
10439 * Fires if an exception occurs in the Proxy during loading.
10440 * Called with the signature of the Proxy's "loadexception" event.
10441 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10444 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10445 * @param {Object} load options
10446 * @param {Object} jsonData from your request (normally this contains the Exception)
10448 loadexception : true
10452 this.proxy = Roo.factory(this.proxy, Roo.data);
10453 this.proxy.xmodule = this.xmodule || false;
10454 this.relayEvents(this.proxy, ["loadexception"]);
10456 this.sortToggle = {};
10457 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10459 Roo.data.Store.superclass.constructor.call(this);
10461 if(this.inlineData){
10462 this.loadData(this.inlineData);
10463 delete this.inlineData;
10467 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10469 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10470 * without a remote query - used by combo/forms at present.
10474 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10477 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10480 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10481 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10484 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10485 * on any HTTP request
10488 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10491 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10495 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10496 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10498 remoteSort : false,
10501 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10502 * loaded or when a record is removed. (defaults to false).
10504 pruneModifiedRecords : false,
10507 lastOptions : null,
10510 * Add Records to the Store and fires the add event.
10511 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10513 add : function(records){
10514 records = [].concat(records);
10515 for(var i = 0, len = records.length; i < len; i++){
10516 records[i].join(this);
10518 var index = this.data.length;
10519 this.data.addAll(records);
10520 this.fireEvent("add", this, records, index);
10524 * Remove a Record from the Store and fires the remove event.
10525 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10527 remove : function(record){
10528 var index = this.data.indexOf(record);
10529 this.data.removeAt(index);
10530 if(this.pruneModifiedRecords){
10531 this.modified.remove(record);
10533 this.fireEvent("remove", this, record, index);
10537 * Remove all Records from the Store and fires the clear event.
10539 removeAll : function(){
10541 if(this.pruneModifiedRecords){
10542 this.modified = [];
10544 this.fireEvent("clear", this);
10548 * Inserts Records to the Store at the given index and fires the add event.
10549 * @param {Number} index The start index at which to insert the passed Records.
10550 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10552 insert : function(index, records){
10553 records = [].concat(records);
10554 for(var i = 0, len = records.length; i < len; i++){
10555 this.data.insert(index, records[i]);
10556 records[i].join(this);
10558 this.fireEvent("add", this, records, index);
10562 * Get the index within the cache of the passed Record.
10563 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10564 * @return {Number} The index of the passed Record. Returns -1 if not found.
10566 indexOf : function(record){
10567 return this.data.indexOf(record);
10571 * Get the index within the cache of the Record with the passed id.
10572 * @param {String} id The id of the Record to find.
10573 * @return {Number} The index of the Record. Returns -1 if not found.
10575 indexOfId : function(id){
10576 return this.data.indexOfKey(id);
10580 * Get the Record with the specified id.
10581 * @param {String} id The id of the Record to find.
10582 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10584 getById : function(id){
10585 return this.data.key(id);
10589 * Get the Record at the specified index.
10590 * @param {Number} index The index of the Record to find.
10591 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10593 getAt : function(index){
10594 return this.data.itemAt(index);
10598 * Returns a range of Records between specified indices.
10599 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10600 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10601 * @return {Roo.data.Record[]} An array of Records
10603 getRange : function(start, end){
10604 return this.data.getRange(start, end);
10608 storeOptions : function(o){
10609 o = Roo.apply({}, o);
10612 this.lastOptions = o;
10616 * Loads the Record cache from the configured Proxy using the configured Reader.
10618 * If using remote paging, then the first load call must specify the <em>start</em>
10619 * and <em>limit</em> properties in the options.params property to establish the initial
10620 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10622 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10623 * and this call will return before the new data has been loaded. Perform any post-processing
10624 * in a callback function, or in a "load" event handler.</strong>
10626 * @param {Object} options An object containing properties which control loading options:<ul>
10627 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10628 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10629 * passed the following arguments:<ul>
10630 * <li>r : Roo.data.Record[]</li>
10631 * <li>options: Options object from the load call</li>
10632 * <li>success: Boolean success indicator</li></ul></li>
10633 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10634 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10637 load : function(options){
10638 options = options || {};
10639 if(this.fireEvent("beforeload", this, options) !== false){
10640 this.storeOptions(options);
10641 var p = Roo.apply(options.params || {}, this.baseParams);
10642 // if meta was not loaded from remote source.. try requesting it.
10643 if (!this.reader.metaFromRemote) {
10644 p._requestMeta = 1;
10646 if(this.sortInfo && this.remoteSort){
10647 var pn = this.paramNames;
10648 p[pn["sort"]] = this.sortInfo.field;
10649 p[pn["dir"]] = this.sortInfo.direction;
10651 if (this.multiSort) {
10652 var pn = this.paramNames;
10653 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10656 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10661 * Reloads the Record cache from the configured Proxy using the configured Reader and
10662 * the options from the last load operation performed.
10663 * @param {Object} options (optional) An object containing properties which may override the options
10664 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10665 * the most recently used options are reused).
10667 reload : function(options){
10668 this.load(Roo.applyIf(options||{}, this.lastOptions));
10672 // Called as a callback by the Reader during a load operation.
10673 loadRecords : function(o, options, success){
10674 if(!o || success === false){
10675 if(success !== false){
10676 this.fireEvent("load", this, [], options, o);
10678 if(options.callback){
10679 options.callback.call(options.scope || this, [], options, false);
10683 // if data returned failure - throw an exception.
10684 if (o.success === false) {
10685 // show a message if no listener is registered.
10686 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10687 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10689 // loadmask wil be hooked into this..
10690 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10693 var r = o.records, t = o.totalRecords || r.length;
10695 this.fireEvent("beforeloadadd", this, r, options, o);
10697 if(!options || options.add !== true){
10698 if(this.pruneModifiedRecords){
10699 this.modified = [];
10701 for(var i = 0, len = r.length; i < len; i++){
10705 this.data = this.snapshot;
10706 delete this.snapshot;
10709 this.data.addAll(r);
10710 this.totalLength = t;
10712 this.fireEvent("datachanged", this);
10714 this.totalLength = Math.max(t, this.data.length+r.length);
10717 this.fireEvent("load", this, r, options, o);
10718 if(options.callback){
10719 options.callback.call(options.scope || this, r, options, true);
10725 * Loads data from a passed data block. A Reader which understands the format of the data
10726 * must have been configured in the constructor.
10727 * @param {Object} data The data block from which to read the Records. The format of the data expected
10728 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10729 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10731 loadData : function(o, append){
10732 var r = this.reader.readRecords(o);
10733 this.loadRecords(r, {add: append}, true);
10737 * Gets the number of cached records.
10739 * <em>If using paging, this may not be the total size of the dataset. If the data object
10740 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10741 * the data set size</em>
10743 getCount : function(){
10744 return this.data.length || 0;
10748 * Gets the total number of records in the dataset as returned by the server.
10750 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10751 * the dataset size</em>
10753 getTotalCount : function(){
10754 return this.totalLength || 0;
10758 * Returns the sort state of the Store as an object with two properties:
10760 field {String} The name of the field by which the Records are sorted
10761 direction {String} The sort order, "ASC" or "DESC"
10764 getSortState : function(){
10765 return this.sortInfo;
10769 applySort : function(){
10770 if(this.sortInfo && !this.remoteSort){
10771 var s = this.sortInfo, f = s.field;
10772 var st = this.fields.get(f).sortType;
10773 var fn = function(r1, r2){
10774 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10775 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10777 this.data.sort(s.direction, fn);
10778 if(this.snapshot && this.snapshot != this.data){
10779 this.snapshot.sort(s.direction, fn);
10785 * Sets the default sort column and order to be used by the next load operation.
10786 * @param {String} fieldName The name of the field to sort by.
10787 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10789 setDefaultSort : function(field, dir){
10790 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10794 * Sort the Records.
10795 * If remote sorting is used, the sort is performed on the server, and the cache is
10796 * reloaded. If local sorting is used, the cache is sorted internally.
10797 * @param {String} fieldName The name of the field to sort by.
10798 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10800 sort : function(fieldName, dir){
10801 var f = this.fields.get(fieldName);
10803 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10805 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10806 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10811 this.sortToggle[f.name] = dir;
10812 this.sortInfo = {field: f.name, direction: dir};
10813 if(!this.remoteSort){
10815 this.fireEvent("datachanged", this);
10817 this.load(this.lastOptions);
10822 * Calls the specified function for each of the Records in the cache.
10823 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10824 * Returning <em>false</em> aborts and exits the iteration.
10825 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10827 each : function(fn, scope){
10828 this.data.each(fn, scope);
10832 * Gets all records modified since the last commit. Modified records are persisted across load operations
10833 * (e.g., during paging).
10834 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10836 getModifiedRecords : function(){
10837 return this.modified;
10841 createFilterFn : function(property, value, anyMatch){
10842 if(!value.exec){ // not a regex
10843 value = String(value);
10844 if(value.length == 0){
10847 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10849 return function(r){
10850 return value.test(r.data[property]);
10855 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10856 * @param {String} property A field on your records
10857 * @param {Number} start The record index to start at (defaults to 0)
10858 * @param {Number} end The last record index to include (defaults to length - 1)
10859 * @return {Number} The sum
10861 sum : function(property, start, end){
10862 var rs = this.data.items, v = 0;
10863 start = start || 0;
10864 end = (end || end === 0) ? end : rs.length-1;
10866 for(var i = start; i <= end; i++){
10867 v += (rs[i].data[property] || 0);
10873 * Filter the records by a specified property.
10874 * @param {String} field A field on your records
10875 * @param {String/RegExp} value Either a string that the field
10876 * should start with or a RegExp to test against the field
10877 * @param {Boolean} anyMatch True to match any part not just the beginning
10879 filter : function(property, value, anyMatch){
10880 var fn = this.createFilterFn(property, value, anyMatch);
10881 return fn ? this.filterBy(fn) : this.clearFilter();
10885 * Filter by a function. The specified function will be called with each
10886 * record in this data source. If the function returns true the record is included,
10887 * otherwise it is filtered.
10888 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10889 * @param {Object} scope (optional) The scope of the function (defaults to this)
10891 filterBy : function(fn, scope){
10892 this.snapshot = this.snapshot || this.data;
10893 this.data = this.queryBy(fn, scope||this);
10894 this.fireEvent("datachanged", this);
10898 * Query the records by a specified property.
10899 * @param {String} field A field on your records
10900 * @param {String/RegExp} value Either a string that the field
10901 * should start with or a RegExp to test against the field
10902 * @param {Boolean} anyMatch True to match any part not just the beginning
10903 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10905 query : function(property, value, anyMatch){
10906 var fn = this.createFilterFn(property, value, anyMatch);
10907 return fn ? this.queryBy(fn) : this.data.clone();
10911 * Query by a function. The specified function will be called with each
10912 * record in this data source. If the function returns true the record is included
10914 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10915 * @param {Object} scope (optional) The scope of the function (defaults to this)
10916 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10918 queryBy : function(fn, scope){
10919 var data = this.snapshot || this.data;
10920 return data.filterBy(fn, scope||this);
10924 * Collects unique values for a particular dataIndex from this store.
10925 * @param {String} dataIndex The property to collect
10926 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10927 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10928 * @return {Array} An array of the unique values
10930 collect : function(dataIndex, allowNull, bypassFilter){
10931 var d = (bypassFilter === true && this.snapshot) ?
10932 this.snapshot.items : this.data.items;
10933 var v, sv, r = [], l = {};
10934 for(var i = 0, len = d.length; i < len; i++){
10935 v = d[i].data[dataIndex];
10937 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10946 * Revert to a view of the Record cache with no filtering applied.
10947 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10949 clearFilter : function(suppressEvent){
10950 if(this.snapshot && this.snapshot != this.data){
10951 this.data = this.snapshot;
10952 delete this.snapshot;
10953 if(suppressEvent !== true){
10954 this.fireEvent("datachanged", this);
10960 afterEdit : function(record){
10961 if(this.modified.indexOf(record) == -1){
10962 this.modified.push(record);
10964 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10968 afterReject : function(record){
10969 this.modified.remove(record);
10970 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10974 afterCommit : function(record){
10975 this.modified.remove(record);
10976 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10980 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10981 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10983 commitChanges : function(){
10984 var m = this.modified.slice(0);
10985 this.modified = [];
10986 for(var i = 0, len = m.length; i < len; i++){
10992 * Cancel outstanding changes on all changed records.
10994 rejectChanges : function(){
10995 var m = this.modified.slice(0);
10996 this.modified = [];
10997 for(var i = 0, len = m.length; i < len; i++){
11002 onMetaChange : function(meta, rtype, o){
11003 this.recordType = rtype;
11004 this.fields = rtype.prototype.fields;
11005 delete this.snapshot;
11006 this.sortInfo = meta.sortInfo || this.sortInfo;
11007 this.modified = [];
11008 this.fireEvent('metachange', this, this.reader.meta);
11011 moveIndex : function(data, type)
11013 var index = this.indexOf(data);
11015 var newIndex = index + type;
11019 this.insert(newIndex, data);
11024 * Ext JS Library 1.1.1
11025 * Copyright(c) 2006-2007, Ext JS, LLC.
11027 * Originally Released Under LGPL - original licence link has changed is not relivant.
11030 * <script type="text/javascript">
11034 * @class Roo.data.SimpleStore
11035 * @extends Roo.data.Store
11036 * Small helper class to make creating Stores from Array data easier.
11037 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11038 * @cfg {Array} fields An array of field definition objects, or field name strings.
11039 * @cfg {Array} data The multi-dimensional array of data
11041 * @param {Object} config
11043 Roo.data.SimpleStore = function(config){
11044 Roo.data.SimpleStore.superclass.constructor.call(this, {
11046 reader: new Roo.data.ArrayReader({
11049 Roo.data.Record.create(config.fields)
11051 proxy : new Roo.data.MemoryProxy(config.data)
11055 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11057 * Ext JS Library 1.1.1
11058 * Copyright(c) 2006-2007, Ext JS, LLC.
11060 * Originally Released Under LGPL - original licence link has changed is not relivant.
11063 * <script type="text/javascript">
11068 * @extends Roo.data.Store
11069 * @class Roo.data.JsonStore
11070 * Small helper class to make creating Stores for JSON data easier. <br/>
11072 var store = new Roo.data.JsonStore({
11073 url: 'get-images.php',
11075 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11078 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11079 * JsonReader and HttpProxy (unless inline data is provided).</b>
11080 * @cfg {Array} fields An array of field definition objects, or field name strings.
11082 * @param {Object} config
11084 Roo.data.JsonStore = function(c){
11085 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11086 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11087 reader: new Roo.data.JsonReader(c, c.fields)
11090 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11092 * Ext JS Library 1.1.1
11093 * Copyright(c) 2006-2007, Ext JS, LLC.
11095 * Originally Released Under LGPL - original licence link has changed is not relivant.
11098 * <script type="text/javascript">
11102 Roo.data.Field = function(config){
11103 if(typeof config == "string"){
11104 config = {name: config};
11106 Roo.apply(this, config);
11109 this.type = "auto";
11112 var st = Roo.data.SortTypes;
11113 // named sortTypes are supported, here we look them up
11114 if(typeof this.sortType == "string"){
11115 this.sortType = st[this.sortType];
11118 // set default sortType for strings and dates
11119 if(!this.sortType){
11122 this.sortType = st.asUCString;
11125 this.sortType = st.asDate;
11128 this.sortType = st.none;
11133 var stripRe = /[\$,%]/g;
11135 // prebuilt conversion function for this field, instead of
11136 // switching every time we're reading a value
11138 var cv, dateFormat = this.dateFormat;
11143 cv = function(v){ return v; };
11146 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11150 return v !== undefined && v !== null && v !== '' ?
11151 parseInt(String(v).replace(stripRe, ""), 10) : '';
11156 return v !== undefined && v !== null && v !== '' ?
11157 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11162 cv = function(v){ return v === true || v === "true" || v == 1; };
11169 if(v instanceof Date){
11173 if(dateFormat == "timestamp"){
11174 return new Date(v*1000);
11176 return Date.parseDate(v, dateFormat);
11178 var parsed = Date.parse(v);
11179 return parsed ? new Date(parsed) : null;
11188 Roo.data.Field.prototype = {
11196 * Ext JS Library 1.1.1
11197 * Copyright(c) 2006-2007, Ext JS, LLC.
11199 * Originally Released Under LGPL - original licence link has changed is not relivant.
11202 * <script type="text/javascript">
11205 // Base class for reading structured data from a data source. This class is intended to be
11206 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11209 * @class Roo.data.DataReader
11210 * Base class for reading structured data from a data source. This class is intended to be
11211 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11214 Roo.data.DataReader = function(meta, recordType){
11218 this.recordType = recordType instanceof Array ?
11219 Roo.data.Record.create(recordType) : recordType;
11222 Roo.data.DataReader.prototype = {
11224 * Create an empty record
11225 * @param {Object} data (optional) - overlay some values
11226 * @return {Roo.data.Record} record created.
11228 newRow : function(d) {
11230 this.recordType.prototype.fields.each(function(c) {
11232 case 'int' : da[c.name] = 0; break;
11233 case 'date' : da[c.name] = new Date(); break;
11234 case 'float' : da[c.name] = 0.0; break;
11235 case 'boolean' : da[c.name] = false; break;
11236 default : da[c.name] = ""; break;
11240 return new this.recordType(Roo.apply(da, d));
11245 * Ext JS Library 1.1.1
11246 * Copyright(c) 2006-2007, Ext JS, LLC.
11248 * Originally Released Under LGPL - original licence link has changed is not relivant.
11251 * <script type="text/javascript">
11255 * @class Roo.data.DataProxy
11256 * @extends Roo.data.Observable
11257 * This class is an abstract base class for implementations which provide retrieval of
11258 * unformatted data objects.<br>
11260 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11261 * (of the appropriate type which knows how to parse the data object) to provide a block of
11262 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11264 * Custom implementations must implement the load method as described in
11265 * {@link Roo.data.HttpProxy#load}.
11267 Roo.data.DataProxy = function(){
11270 * @event beforeload
11271 * Fires before a network request is made to retrieve a data object.
11272 * @param {Object} This DataProxy object.
11273 * @param {Object} params The params parameter to the load function.
11278 * Fires before the load method's callback is called.
11279 * @param {Object} This DataProxy object.
11280 * @param {Object} o The data object.
11281 * @param {Object} arg The callback argument object passed to the load function.
11285 * @event loadexception
11286 * Fires if an Exception occurs during data retrieval.
11287 * @param {Object} This DataProxy object.
11288 * @param {Object} o The data object.
11289 * @param {Object} arg The callback argument object passed to the load function.
11290 * @param {Object} e The Exception.
11292 loadexception : true
11294 Roo.data.DataProxy.superclass.constructor.call(this);
11297 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11300 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11304 * Ext JS Library 1.1.1
11305 * Copyright(c) 2006-2007, Ext JS, LLC.
11307 * Originally Released Under LGPL - original licence link has changed is not relivant.
11310 * <script type="text/javascript">
11313 * @class Roo.data.MemoryProxy
11314 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11315 * to the Reader when its load method is called.
11317 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11319 Roo.data.MemoryProxy = function(data){
11323 Roo.data.MemoryProxy.superclass.constructor.call(this);
11327 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11330 * Load data from the requested source (in this case an in-memory
11331 * data object passed to the constructor), read the data object into
11332 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11333 * process that block using the passed callback.
11334 * @param {Object} params This parameter is not used by the MemoryProxy class.
11335 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11336 * object into a block of Roo.data.Records.
11337 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11338 * The function must be passed <ul>
11339 * <li>The Record block object</li>
11340 * <li>The "arg" argument from the load function</li>
11341 * <li>A boolean success indicator</li>
11343 * @param {Object} scope The scope in which to call the callback
11344 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11346 load : function(params, reader, callback, scope, arg){
11347 params = params || {};
11350 result = reader.readRecords(this.data);
11352 this.fireEvent("loadexception", this, arg, null, e);
11353 callback.call(scope, null, arg, false);
11356 callback.call(scope, result, arg, true);
11360 update : function(params, records){
11365 * Ext JS Library 1.1.1
11366 * Copyright(c) 2006-2007, Ext JS, LLC.
11368 * Originally Released Under LGPL - original licence link has changed is not relivant.
11371 * <script type="text/javascript">
11374 * @class Roo.data.HttpProxy
11375 * @extends Roo.data.DataProxy
11376 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11377 * configured to reference a certain URL.<br><br>
11379 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11380 * from which the running page was served.<br><br>
11382 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11384 * Be aware that to enable the browser to parse an XML document, the server must set
11385 * the Content-Type header in the HTTP response to "text/xml".
11387 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11388 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11389 * will be used to make the request.
11391 Roo.data.HttpProxy = function(conn){
11392 Roo.data.HttpProxy.superclass.constructor.call(this);
11393 // is conn a conn config or a real conn?
11395 this.useAjax = !conn || !conn.events;
11399 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11400 // thse are take from connection...
11403 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11406 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11407 * extra parameters to each request made by this object. (defaults to undefined)
11410 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11411 * to each request made by this object. (defaults to undefined)
11414 * @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)
11417 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11420 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11426 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11430 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11431 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11432 * a finer-grained basis than the DataProxy events.
11434 getConnection : function(){
11435 return this.useAjax ? Roo.Ajax : this.conn;
11439 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11440 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11441 * process that block using the passed callback.
11442 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11443 * for the request to the remote server.
11444 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11445 * object into a block of Roo.data.Records.
11446 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11447 * The function must be passed <ul>
11448 * <li>The Record block object</li>
11449 * <li>The "arg" argument from the load function</li>
11450 * <li>A boolean success indicator</li>
11452 * @param {Object} scope The scope in which to call the callback
11453 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11455 load : function(params, reader, callback, scope, arg){
11456 if(this.fireEvent("beforeload", this, params) !== false){
11458 params : params || {},
11460 callback : callback,
11465 callback : this.loadResponse,
11469 Roo.applyIf(o, this.conn);
11470 if(this.activeRequest){
11471 Roo.Ajax.abort(this.activeRequest);
11473 this.activeRequest = Roo.Ajax.request(o);
11475 this.conn.request(o);
11478 callback.call(scope||this, null, arg, false);
11483 loadResponse : function(o, success, response){
11484 delete this.activeRequest;
11486 this.fireEvent("loadexception", this, o, response);
11487 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11492 result = o.reader.read(response);
11494 this.fireEvent("loadexception", this, o, response, e);
11495 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11499 this.fireEvent("load", this, o, o.request.arg);
11500 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11504 update : function(dataSet){
11509 updateResponse : function(dataSet){
11514 * Ext JS Library 1.1.1
11515 * Copyright(c) 2006-2007, Ext JS, LLC.
11517 * Originally Released Under LGPL - original licence link has changed is not relivant.
11520 * <script type="text/javascript">
11524 * @class Roo.data.ScriptTagProxy
11525 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11526 * other than the originating domain of the running page.<br><br>
11528 * <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
11529 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11531 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11532 * source code that is used as the source inside a <script> tag.<br><br>
11534 * In order for the browser to process the returned data, the server must wrap the data object
11535 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11536 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11537 * depending on whether the callback name was passed:
11540 boolean scriptTag = false;
11541 String cb = request.getParameter("callback");
11544 response.setContentType("text/javascript");
11546 response.setContentType("application/x-json");
11548 Writer out = response.getWriter();
11550 out.write(cb + "(");
11552 out.print(dataBlock.toJsonString());
11559 * @param {Object} config A configuration object.
11561 Roo.data.ScriptTagProxy = function(config){
11562 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11563 Roo.apply(this, config);
11564 this.head = document.getElementsByTagName("head")[0];
11567 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11569 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11571 * @cfg {String} url The URL from which to request the data object.
11574 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11578 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11579 * the server the name of the callback function set up by the load call to process the returned data object.
11580 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11581 * javascript output which calls this named function passing the data object as its only parameter.
11583 callbackParam : "callback",
11585 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11586 * name to the request.
11591 * Load data from the configured URL, read the data object into
11592 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11593 * process that block using the passed callback.
11594 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11595 * for the request to the remote server.
11596 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11597 * object into a block of Roo.data.Records.
11598 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11599 * The function must be passed <ul>
11600 * <li>The Record block object</li>
11601 * <li>The "arg" argument from the load function</li>
11602 * <li>A boolean success indicator</li>
11604 * @param {Object} scope The scope in which to call the callback
11605 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11607 load : function(params, reader, callback, scope, arg){
11608 if(this.fireEvent("beforeload", this, params) !== false){
11610 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11612 var url = this.url;
11613 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11615 url += "&_dc=" + (new Date().getTime());
11617 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11620 cb : "stcCallback"+transId,
11621 scriptId : "stcScript"+transId,
11625 callback : callback,
11631 window[trans.cb] = function(o){
11632 conn.handleResponse(o, trans);
11635 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11637 if(this.autoAbort !== false){
11641 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11643 var script = document.createElement("script");
11644 script.setAttribute("src", url);
11645 script.setAttribute("type", "text/javascript");
11646 script.setAttribute("id", trans.scriptId);
11647 this.head.appendChild(script);
11649 this.trans = trans;
11651 callback.call(scope||this, null, arg, false);
11656 isLoading : function(){
11657 return this.trans ? true : false;
11661 * Abort the current server request.
11663 abort : function(){
11664 if(this.isLoading()){
11665 this.destroyTrans(this.trans);
11670 destroyTrans : function(trans, isLoaded){
11671 this.head.removeChild(document.getElementById(trans.scriptId));
11672 clearTimeout(trans.timeoutId);
11674 window[trans.cb] = undefined;
11676 delete window[trans.cb];
11679 // if hasn't been loaded, wait for load to remove it to prevent script error
11680 window[trans.cb] = function(){
11681 window[trans.cb] = undefined;
11683 delete window[trans.cb];
11690 handleResponse : function(o, trans){
11691 this.trans = false;
11692 this.destroyTrans(trans, true);
11695 result = trans.reader.readRecords(o);
11697 this.fireEvent("loadexception", this, o, trans.arg, e);
11698 trans.callback.call(trans.scope||window, null, trans.arg, false);
11701 this.fireEvent("load", this, o, trans.arg);
11702 trans.callback.call(trans.scope||window, result, trans.arg, true);
11706 handleFailure : function(trans){
11707 this.trans = false;
11708 this.destroyTrans(trans, false);
11709 this.fireEvent("loadexception", this, null, trans.arg);
11710 trans.callback.call(trans.scope||window, null, trans.arg, false);
11714 * Ext JS Library 1.1.1
11715 * Copyright(c) 2006-2007, Ext JS, LLC.
11717 * Originally Released Under LGPL - original licence link has changed is not relivant.
11720 * <script type="text/javascript">
11724 * @class Roo.data.JsonReader
11725 * @extends Roo.data.DataReader
11726 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11727 * based on mappings in a provided Roo.data.Record constructor.
11729 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11730 * in the reply previously.
11735 var RecordDef = Roo.data.Record.create([
11736 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11737 {name: 'occupation'} // This field will use "occupation" as the mapping.
11739 var myReader = new Roo.data.JsonReader({
11740 totalProperty: "results", // The property which contains the total dataset size (optional)
11741 root: "rows", // The property which contains an Array of row objects
11742 id: "id" // The property within each row object that provides an ID for the record (optional)
11746 * This would consume a JSON file like this:
11748 { 'results': 2, 'rows': [
11749 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11750 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11753 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11754 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11755 * paged from the remote server.
11756 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11757 * @cfg {String} root name of the property which contains the Array of row objects.
11758 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11759 * @cfg {Array} fields Array of field definition objects
11761 * Create a new JsonReader
11762 * @param {Object} meta Metadata configuration options
11763 * @param {Object} recordType Either an Array of field definition objects,
11764 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11766 Roo.data.JsonReader = function(meta, recordType){
11769 // set some defaults:
11770 Roo.applyIf(meta, {
11771 totalProperty: 'total',
11772 successProperty : 'success',
11777 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11779 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11782 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11783 * Used by Store query builder to append _requestMeta to params.
11786 metaFromRemote : false,
11788 * This method is only used by a DataProxy which has retrieved data from a remote server.
11789 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11790 * @return {Object} data A data block which is used by an Roo.data.Store object as
11791 * a cache of Roo.data.Records.
11793 read : function(response){
11794 var json = response.responseText;
11796 var o = /* eval:var:o */ eval("("+json+")");
11798 throw {message: "JsonReader.read: Json object not found"};
11804 this.metaFromRemote = true;
11805 this.meta = o.metaData;
11806 this.recordType = Roo.data.Record.create(o.metaData.fields);
11807 this.onMetaChange(this.meta, this.recordType, o);
11809 return this.readRecords(o);
11812 // private function a store will implement
11813 onMetaChange : function(meta, recordType, o){
11820 simpleAccess: function(obj, subsc) {
11827 getJsonAccessor: function(){
11829 return function(expr) {
11831 return(re.test(expr))
11832 ? new Function("obj", "return obj." + expr)
11837 return Roo.emptyFn;
11842 * Create a data block containing Roo.data.Records from an XML document.
11843 * @param {Object} o An object which contains an Array of row objects in the property specified
11844 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11845 * which contains the total size of the dataset.
11846 * @return {Object} data A data block which is used by an Roo.data.Store object as
11847 * a cache of Roo.data.Records.
11849 readRecords : function(o){
11851 * After any data loads, the raw JSON data is available for further custom processing.
11855 var s = this.meta, Record = this.recordType,
11856 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11858 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11860 if(s.totalProperty) {
11861 this.getTotal = this.getJsonAccessor(s.totalProperty);
11863 if(s.successProperty) {
11864 this.getSuccess = this.getJsonAccessor(s.successProperty);
11866 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11868 var g = this.getJsonAccessor(s.id);
11869 this.getId = function(rec) {
11871 return (r === undefined || r === "") ? null : r;
11874 this.getId = function(){return null;};
11877 for(var jj = 0; jj < fl; jj++){
11879 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11880 this.ef[jj] = this.getJsonAccessor(map);
11884 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11885 if(s.totalProperty){
11886 var vt = parseInt(this.getTotal(o), 10);
11891 if(s.successProperty){
11892 var vs = this.getSuccess(o);
11893 if(vs === false || vs === 'false'){
11898 for(var i = 0; i < c; i++){
11901 var id = this.getId(n);
11902 for(var j = 0; j < fl; j++){
11904 var v = this.ef[j](n);
11906 Roo.log('missing convert for ' + f.name);
11910 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11912 var record = new Record(values, id);
11914 records[i] = record;
11920 totalRecords : totalRecords
11925 * Ext JS Library 1.1.1
11926 * Copyright(c) 2006-2007, Ext JS, LLC.
11928 * Originally Released Under LGPL - original licence link has changed is not relivant.
11931 * <script type="text/javascript">
11935 * @class Roo.data.ArrayReader
11936 * @extends Roo.data.DataReader
11937 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11938 * Each element of that Array represents a row of data fields. The
11939 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11940 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11944 var RecordDef = Roo.data.Record.create([
11945 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11946 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11948 var myReader = new Roo.data.ArrayReader({
11949 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11953 * This would consume an Array like this:
11955 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11957 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11959 * Create a new JsonReader
11960 * @param {Object} meta Metadata configuration options.
11961 * @param {Object} recordType Either an Array of field definition objects
11962 * as specified to {@link Roo.data.Record#create},
11963 * or an {@link Roo.data.Record} object
11964 * created using {@link Roo.data.Record#create}.
11966 Roo.data.ArrayReader = function(meta, recordType){
11967 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11970 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11972 * Create a data block containing Roo.data.Records from an XML document.
11973 * @param {Object} o An Array of row objects which represents the dataset.
11974 * @return {Object} data A data block which is used by an Roo.data.Store object as
11975 * a cache of Roo.data.Records.
11977 readRecords : function(o){
11978 var sid = this.meta ? this.meta.id : null;
11979 var recordType = this.recordType, fields = recordType.prototype.fields;
11982 for(var i = 0; i < root.length; i++){
11985 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11986 for(var j = 0, jlen = fields.length; j < jlen; j++){
11987 var f = fields.items[j];
11988 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11989 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11991 values[f.name] = v;
11993 var record = new recordType(values, id);
11995 records[records.length] = record;
11999 totalRecords : records.length
12008 * @class Roo.bootstrap.ComboBox
12009 * @extends Roo.bootstrap.TriggerField
12010 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12011 * @cfg {Boolean} append (true|false) default false
12012 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12013 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12014 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12015 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12016 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12017 * @cfg {Boolean} animate default true
12018 * @cfg {Boolean} emptyResultText only for touch device
12019 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12021 * Create a new ComboBox.
12022 * @param {Object} config Configuration options
12024 Roo.bootstrap.ComboBox = function(config){
12025 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12029 * Fires when the dropdown list is expanded
12030 * @param {Roo.bootstrap.ComboBox} combo This combo box
12035 * Fires when the dropdown list is collapsed
12036 * @param {Roo.bootstrap.ComboBox} combo This combo box
12040 * @event beforeselect
12041 * Fires before a list item is selected. Return false to cancel the selection.
12042 * @param {Roo.bootstrap.ComboBox} combo This combo box
12043 * @param {Roo.data.Record} record The data record returned from the underlying store
12044 * @param {Number} index The index of the selected item in the dropdown list
12046 'beforeselect' : true,
12049 * Fires when a list item is selected
12050 * @param {Roo.bootstrap.ComboBox} combo This combo box
12051 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12052 * @param {Number} index The index of the selected item in the dropdown list
12056 * @event beforequery
12057 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12058 * The event object passed has these properties:
12059 * @param {Roo.bootstrap.ComboBox} combo This combo box
12060 * @param {String} query The query
12061 * @param {Boolean} forceAll true to force "all" query
12062 * @param {Boolean} cancel true to cancel the query
12063 * @param {Object} e The query event object
12065 'beforequery': true,
12068 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12069 * @param {Roo.bootstrap.ComboBox} combo This combo box
12074 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12075 * @param {Roo.bootstrap.ComboBox} combo This combo box
12076 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12081 * Fires when the remove value from the combobox array
12082 * @param {Roo.bootstrap.ComboBox} combo This combo box
12086 * @event afterremove
12087 * Fires when the remove value from the combobox array
12088 * @param {Roo.bootstrap.ComboBox} combo This combo box
12090 'afterremove' : true,
12092 * @event specialfilter
12093 * Fires when specialfilter
12094 * @param {Roo.bootstrap.ComboBox} combo This combo box
12096 'specialfilter' : true,
12099 * Fires when tick the element
12100 * @param {Roo.bootstrap.ComboBox} combo This combo box
12104 * @event touchviewdisplay
12105 * Fires when touch view require special display (default is using displayField)
12106 * @param {Roo.bootstrap.ComboBox} combo This combo box
12107 * @param {Object} cfg set html .
12109 'touchviewdisplay' : true
12114 this.tickItems = [];
12116 this.selectedIndex = -1;
12117 if(this.mode == 'local'){
12118 if(config.queryDelay === undefined){
12119 this.queryDelay = 10;
12121 if(config.minChars === undefined){
12127 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12130 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12131 * rendering into an Roo.Editor, defaults to false)
12134 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12135 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12138 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12141 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12142 * the dropdown list (defaults to undefined, with no header element)
12146 * @cfg {String/Roo.Template} tpl The template to use to render the output
12150 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12152 listWidth: undefined,
12154 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12155 * mode = 'remote' or 'text' if mode = 'local')
12157 displayField: undefined,
12160 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12161 * mode = 'remote' or 'value' if mode = 'local').
12162 * Note: use of a valueField requires the user make a selection
12163 * in order for a value to be mapped.
12165 valueField: undefined,
12167 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12172 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12173 * field's data value (defaults to the underlying DOM element's name)
12175 hiddenName: undefined,
12177 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12181 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12183 selectedClass: 'active',
12186 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12190 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12191 * anchor positions (defaults to 'tl-bl')
12193 listAlign: 'tl-bl?',
12195 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12199 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12200 * query specified by the allQuery config option (defaults to 'query')
12202 triggerAction: 'query',
12204 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12205 * (defaults to 4, does not apply if editable = false)
12209 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12210 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12214 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12215 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12219 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12220 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12224 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12225 * when editable = true (defaults to false)
12227 selectOnFocus:false,
12229 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12231 queryParam: 'query',
12233 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12234 * when mode = 'remote' (defaults to 'Loading...')
12236 loadingText: 'Loading...',
12238 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12242 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12246 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12247 * traditional select (defaults to true)
12251 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12255 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12259 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12260 * listWidth has a higher value)
12264 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12265 * allow the user to set arbitrary text into the field (defaults to false)
12267 forceSelection:false,
12269 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12270 * if typeAhead = true (defaults to 250)
12272 typeAheadDelay : 250,
12274 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12275 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12277 valueNotFoundText : undefined,
12279 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12281 blockFocus : false,
12284 * @cfg {Boolean} disableClear Disable showing of clear button.
12286 disableClear : false,
12288 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12290 alwaysQuery : false,
12293 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12298 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12300 invalidClass : "has-warning",
12303 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12305 validClass : "has-success",
12308 * @cfg {Boolean} specialFilter (true|false) special filter default false
12310 specialFilter : false,
12313 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12315 mobileTouchView : true,
12318 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12320 useNativeIOS : false,
12322 ios_options : false,
12334 btnPosition : 'right',
12335 triggerList : true,
12336 showToggleBtn : true,
12338 emptyResultText: 'Empty',
12339 triggerText : 'Select',
12341 // element that contains real text value.. (when hidden is used..)
12343 getAutoCreate : function()
12348 * Render classic select for iso
12351 if(Roo.isIOS && this.useNativeIOS){
12352 cfg = this.getAutoCreateNativeIOS();
12360 if(Roo.isTouch && this.mobileTouchView){
12361 cfg = this.getAutoCreateTouchView();
12368 if(!this.tickable){
12369 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12374 * ComboBox with tickable selections
12377 var align = this.labelAlign || this.parentLabelAlign();
12380 cls : 'form-group roo-combobox-tickable' //input-group
12383 var btn_text_select = '';
12384 var btn_text_done = '';
12385 var btn_text_cancel = '';
12387 if (this.btn_text_show) {
12388 btn_text_select = 'Select';
12389 btn_text_done = 'Done';
12390 btn_text_cancel = 'Cancel';
12395 cls : 'tickable-buttons',
12400 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12401 //html : this.triggerText
12402 html: btn_text_select
12408 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12410 html: btn_text_done
12416 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12418 html: btn_text_cancel
12424 buttons.cn.unshift({
12426 cls: 'roo-select2-search-field-input'
12432 Roo.each(buttons.cn, function(c){
12434 c.cls += ' btn-' + _this.size;
12437 if (_this.disabled) {
12448 cls: 'form-hidden-field'
12452 cls: 'roo-select2-choices',
12456 cls: 'roo-select2-search-field',
12468 cls: 'roo-select2-container input-group roo-select2-container-multi',
12473 // cls: 'typeahead typeahead-long dropdown-menu',
12474 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12479 if(this.hasFeedback && !this.allowBlank){
12483 cls: 'glyphicon form-control-feedback'
12486 combobox.cn.push(feedback);
12489 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12491 // Roo.log("left and has label");
12495 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12496 tooltip : 'This field is required'
12501 cls : 'control-label col-sm-' + this.labelWidth,
12502 html : this.fieldLabel
12506 cls : "col-sm-" + (12 - this.labelWidth),
12514 if(this.indicatorpos == 'right'){
12520 cls : 'control-label col-sm-' + this.labelWidth,
12521 html : this.fieldLabel
12526 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12527 tooltip : 'This field is required'
12530 cls : "col-sm-" + (12 - this.labelWidth),
12541 } else if ( this.fieldLabel.length) {
12542 // Roo.log(" label");
12546 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12547 tooltip : 'This field is required'
12551 //cls : 'input-group-addon',
12552 html : this.fieldLabel
12560 if(this.indicatorpos == 'right'){
12565 //cls : 'input-group-addon',
12566 html : this.fieldLabel
12572 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12573 tooltip : 'This field is required'
12584 // Roo.log(" no label && no align");
12591 ['xs','sm','md','lg'].map(function(size){
12592 if (settings[size]) {
12593 cfg.cls += ' col-' + size + '-' + settings[size];
12601 _initEventsCalled : false,
12604 initEvents: function()
12606 if (this._initEventsCalled) { // as we call render... prevent looping...
12609 this._initEventsCalled = true;
12612 throw "can not find store for combo";
12615 this.store = Roo.factory(this.store, Roo.data);
12617 // if we are building from html. then this element is so complex, that we can not really
12618 // use the rendered HTML.
12619 // so we have to trash and replace the previous code.
12620 if (Roo.XComponent.build_from_html) {
12622 // remove this element....
12623 var e = this.el.dom, k=0;
12624 while (e ) { e = e.previousSibling; ++k;}
12629 this.rendered = false;
12631 this.render(this.parent().getChildContainer(true), k);
12637 if(Roo.isIOS && this.useNativeIOS){
12638 this.initIOSView();
12646 if(Roo.isTouch && this.mobileTouchView){
12647 this.initTouchView();
12652 this.initTickableEvents();
12656 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12658 if(this.hiddenName){
12660 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12662 this.hiddenField.dom.value =
12663 this.hiddenValue !== undefined ? this.hiddenValue :
12664 this.value !== undefined ? this.value : '';
12666 // prevent input submission
12667 this.el.dom.removeAttribute('name');
12668 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12673 // this.el.dom.setAttribute('autocomplete', 'off');
12676 var cls = 'x-combo-list';
12678 //this.list = new Roo.Layer({
12679 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12685 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12686 _this.list.setWidth(lw);
12689 this.list.on('mouseover', this.onViewOver, this);
12690 this.list.on('mousemove', this.onViewMove, this);
12692 this.list.on('scroll', this.onViewScroll, this);
12695 this.list.swallowEvent('mousewheel');
12696 this.assetHeight = 0;
12699 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12700 this.assetHeight += this.header.getHeight();
12703 this.innerList = this.list.createChild({cls:cls+'-inner'});
12704 this.innerList.on('mouseover', this.onViewOver, this);
12705 this.innerList.on('mousemove', this.onViewMove, this);
12706 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12708 if(this.allowBlank && !this.pageSize && !this.disableClear){
12709 this.footer = this.list.createChild({cls:cls+'-ft'});
12710 this.pageTb = new Roo.Toolbar(this.footer);
12714 this.footer = this.list.createChild({cls:cls+'-ft'});
12715 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12716 {pageSize: this.pageSize});
12720 if (this.pageTb && this.allowBlank && !this.disableClear) {
12722 this.pageTb.add(new Roo.Toolbar.Fill(), {
12723 cls: 'x-btn-icon x-btn-clear',
12725 handler: function()
12728 _this.clearValue();
12729 _this.onSelect(false, -1);
12734 this.assetHeight += this.footer.getHeight();
12739 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12742 this.view = new Roo.View(this.list, this.tpl, {
12743 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12745 //this.view.wrapEl.setDisplayed(false);
12746 this.view.on('click', this.onViewClick, this);
12750 this.store.on('beforeload', this.onBeforeLoad, this);
12751 this.store.on('load', this.onLoad, this);
12752 this.store.on('loadexception', this.onLoadException, this);
12754 if(this.resizable){
12755 this.resizer = new Roo.Resizable(this.list, {
12756 pinned:true, handles:'se'
12758 this.resizer.on('resize', function(r, w, h){
12759 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12760 this.listWidth = w;
12761 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12762 this.restrictHeight();
12764 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12767 if(!this.editable){
12768 this.editable = true;
12769 this.setEditable(false);
12774 if (typeof(this.events.add.listeners) != 'undefined') {
12776 this.addicon = this.wrap.createChild(
12777 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12779 this.addicon.on('click', function(e) {
12780 this.fireEvent('add', this);
12783 if (typeof(this.events.edit.listeners) != 'undefined') {
12785 this.editicon = this.wrap.createChild(
12786 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12787 if (this.addicon) {
12788 this.editicon.setStyle('margin-left', '40px');
12790 this.editicon.on('click', function(e) {
12792 // we fire even if inothing is selected..
12793 this.fireEvent('edit', this, this.lastData );
12799 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12800 "up" : function(e){
12801 this.inKeyMode = true;
12805 "down" : function(e){
12806 if(!this.isExpanded()){
12807 this.onTriggerClick();
12809 this.inKeyMode = true;
12814 "enter" : function(e){
12815 // this.onViewClick();
12819 if(this.fireEvent("specialkey", this, e)){
12820 this.onViewClick(false);
12826 "esc" : function(e){
12830 "tab" : function(e){
12833 if(this.fireEvent("specialkey", this, e)){
12834 this.onViewClick(false);
12842 doRelay : function(foo, bar, hname){
12843 if(hname == 'down' || this.scope.isExpanded()){
12844 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12853 this.queryDelay = Math.max(this.queryDelay || 10,
12854 this.mode == 'local' ? 10 : 250);
12857 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12859 if(this.typeAhead){
12860 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12862 if(this.editable !== false){
12863 this.inputEl().on("keyup", this.onKeyUp, this);
12865 if(this.forceSelection){
12866 this.inputEl().on('blur', this.doForce, this);
12870 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12871 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12875 initTickableEvents: function()
12879 if(this.hiddenName){
12881 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12883 this.hiddenField.dom.value =
12884 this.hiddenValue !== undefined ? this.hiddenValue :
12885 this.value !== undefined ? this.value : '';
12887 // prevent input submission
12888 this.el.dom.removeAttribute('name');
12889 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12894 // this.list = this.el.select('ul.dropdown-menu',true).first();
12896 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12897 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12898 if(this.triggerList){
12899 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12902 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12903 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12905 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12906 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12908 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12909 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12911 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12912 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12913 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12916 this.cancelBtn.hide();
12921 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12922 _this.list.setWidth(lw);
12925 this.list.on('mouseover', this.onViewOver, this);
12926 this.list.on('mousemove', this.onViewMove, this);
12928 this.list.on('scroll', this.onViewScroll, this);
12931 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></li>';
12934 this.view = new Roo.View(this.list, this.tpl, {
12935 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12938 //this.view.wrapEl.setDisplayed(false);
12939 this.view.on('click', this.onViewClick, this);
12943 this.store.on('beforeload', this.onBeforeLoad, this);
12944 this.store.on('load', this.onLoad, this);
12945 this.store.on('loadexception', this.onLoadException, this);
12948 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12949 "up" : function(e){
12950 this.inKeyMode = true;
12954 "down" : function(e){
12955 this.inKeyMode = true;
12959 "enter" : function(e){
12960 if(this.fireEvent("specialkey", this, e)){
12961 this.onViewClick(false);
12967 "esc" : function(e){
12968 this.onTickableFooterButtonClick(e, false, false);
12971 "tab" : function(e){
12972 this.fireEvent("specialkey", this, e);
12974 this.onTickableFooterButtonClick(e, false, false);
12981 doRelay : function(e, fn, key){
12982 if(this.scope.isExpanded()){
12983 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12992 this.queryDelay = Math.max(this.queryDelay || 10,
12993 this.mode == 'local' ? 10 : 250);
12996 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12998 if(this.typeAhead){
12999 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13002 if(this.editable !== false){
13003 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13008 onDestroy : function(){
13010 this.view.setStore(null);
13011 this.view.el.removeAllListeners();
13012 this.view.el.remove();
13013 this.view.purgeListeners();
13016 this.list.dom.innerHTML = '';
13020 this.store.un('beforeload', this.onBeforeLoad, this);
13021 this.store.un('load', this.onLoad, this);
13022 this.store.un('loadexception', this.onLoadException, this);
13024 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13028 fireKey : function(e){
13029 if(e.isNavKeyPress() && !this.list.isVisible()){
13030 this.fireEvent("specialkey", this, e);
13035 onResize: function(w, h){
13036 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13038 // if(typeof w != 'number'){
13039 // // we do not handle it!?!?
13042 // var tw = this.trigger.getWidth();
13043 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13044 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13046 // this.inputEl().setWidth( this.adjustWidth('input', x));
13048 // //this.trigger.setStyle('left', x+'px');
13050 // if(this.list && this.listWidth === undefined){
13051 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13052 // this.list.setWidth(lw);
13053 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13061 * Allow or prevent the user from directly editing the field text. If false is passed,
13062 * the user will only be able to select from the items defined in the dropdown list. This method
13063 * is the runtime equivalent of setting the 'editable' config option at config time.
13064 * @param {Boolean} value True to allow the user to directly edit the field text
13066 setEditable : function(value){
13067 if(value == this.editable){
13070 this.editable = value;
13072 this.inputEl().dom.setAttribute('readOnly', true);
13073 this.inputEl().on('mousedown', this.onTriggerClick, this);
13074 this.inputEl().addClass('x-combo-noedit');
13076 this.inputEl().dom.setAttribute('readOnly', false);
13077 this.inputEl().un('mousedown', this.onTriggerClick, this);
13078 this.inputEl().removeClass('x-combo-noedit');
13084 onBeforeLoad : function(combo,opts){
13085 if(!this.hasFocus){
13089 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13091 this.restrictHeight();
13092 this.selectedIndex = -1;
13096 onLoad : function(){
13098 this.hasQuery = false;
13100 if(!this.hasFocus){
13104 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13105 this.loading.hide();
13108 if(this.store.getCount() > 0){
13110 this.restrictHeight();
13111 if(this.lastQuery == this.allQuery){
13112 if(this.editable && !this.tickable){
13113 this.inputEl().dom.select();
13117 !this.selectByValue(this.value, true) &&
13120 !this.store.lastOptions ||
13121 typeof(this.store.lastOptions.add) == 'undefined' ||
13122 this.store.lastOptions.add != true
13125 this.select(0, true);
13128 if(this.autoFocus){
13131 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13132 this.taTask.delay(this.typeAheadDelay);
13136 this.onEmptyResults();
13142 onLoadException : function()
13144 this.hasQuery = false;
13146 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13147 this.loading.hide();
13150 if(this.tickable && this.editable){
13155 // only causes errors at present
13156 //Roo.log(this.store.reader.jsonData);
13157 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13159 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13165 onTypeAhead : function(){
13166 if(this.store.getCount() > 0){
13167 var r = this.store.getAt(0);
13168 var newValue = r.data[this.displayField];
13169 var len = newValue.length;
13170 var selStart = this.getRawValue().length;
13172 if(selStart != len){
13173 this.setRawValue(newValue);
13174 this.selectText(selStart, newValue.length);
13180 onSelect : function(record, index){
13182 if(this.fireEvent('beforeselect', this, record, index) !== false){
13184 this.setFromData(index > -1 ? record.data : false);
13187 this.fireEvent('select', this, record, index);
13192 * Returns the currently selected field value or empty string if no value is set.
13193 * @return {String} value The selected value
13195 getValue : function()
13197 if(Roo.isIOS && this.useNativeIOS){
13198 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13202 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13205 if(this.valueField){
13206 return typeof this.value != 'undefined' ? this.value : '';
13208 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13212 getRawValue : function()
13214 if(Roo.isIOS && this.useNativeIOS){
13215 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13218 var v = this.inputEl().getValue();
13224 * Clears any text/value currently set in the field
13226 clearValue : function(){
13228 if(this.hiddenField){
13229 this.hiddenField.dom.value = '';
13232 this.setRawValue('');
13233 this.lastSelectionText = '';
13234 this.lastData = false;
13236 var close = this.closeTriggerEl();
13247 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13248 * will be displayed in the field. If the value does not match the data value of an existing item,
13249 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13250 * Otherwise the field will be blank (although the value will still be set).
13251 * @param {String} value The value to match
13253 setValue : function(v)
13255 if(Roo.isIOS && this.useNativeIOS){
13256 this.setIOSValue(v);
13266 if(this.valueField){
13267 var r = this.findRecord(this.valueField, v);
13269 text = r.data[this.displayField];
13270 }else if(this.valueNotFoundText !== undefined){
13271 text = this.valueNotFoundText;
13274 this.lastSelectionText = text;
13275 if(this.hiddenField){
13276 this.hiddenField.dom.value = v;
13278 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13281 var close = this.closeTriggerEl();
13284 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13290 * @property {Object} the last set data for the element
13295 * Sets the value of the field based on a object which is related to the record format for the store.
13296 * @param {Object} value the value to set as. or false on reset?
13298 setFromData : function(o){
13305 var dv = ''; // display value
13306 var vv = ''; // value value..
13308 if (this.displayField) {
13309 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13311 // this is an error condition!!!
13312 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13315 if(this.valueField){
13316 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13319 var close = this.closeTriggerEl();
13322 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13325 if(this.hiddenField){
13326 this.hiddenField.dom.value = vv;
13328 this.lastSelectionText = dv;
13329 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13333 // no hidden field.. - we store the value in 'value', but still display
13334 // display field!!!!
13335 this.lastSelectionText = dv;
13336 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13343 reset : function(){
13344 // overridden so that last data is reset..
13351 this.setValue(this.originalValue);
13352 //this.clearInvalid();
13353 this.lastData = false;
13355 this.view.clearSelections();
13361 findRecord : function(prop, value){
13363 if(this.store.getCount() > 0){
13364 this.store.each(function(r){
13365 if(r.data[prop] == value){
13375 getName: function()
13377 // returns hidden if it's set..
13378 if (!this.rendered) {return ''};
13379 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13383 onViewMove : function(e, t){
13384 this.inKeyMode = false;
13388 onViewOver : function(e, t){
13389 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13392 var item = this.view.findItemFromChild(t);
13395 var index = this.view.indexOf(item);
13396 this.select(index, false);
13401 onViewClick : function(view, doFocus, el, e)
13403 var index = this.view.getSelectedIndexes()[0];
13405 var r = this.store.getAt(index);
13409 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13416 Roo.each(this.tickItems, function(v,k){
13418 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13420 _this.tickItems.splice(k, 1);
13422 if(typeof(e) == 'undefined' && view == false){
13423 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13435 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13436 this.tickItems.push(r.data);
13439 if(typeof(e) == 'undefined' && view == false){
13440 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13447 this.onSelect(r, index);
13449 if(doFocus !== false && !this.blockFocus){
13450 this.inputEl().focus();
13455 restrictHeight : function(){
13456 //this.innerList.dom.style.height = '';
13457 //var inner = this.innerList.dom;
13458 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13459 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13460 //this.list.beginUpdate();
13461 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13462 this.list.alignTo(this.inputEl(), this.listAlign);
13463 this.list.alignTo(this.inputEl(), this.listAlign);
13464 //this.list.endUpdate();
13468 onEmptyResults : function(){
13470 if(this.tickable && this.editable){
13471 this.restrictHeight();
13479 * Returns true if the dropdown list is expanded, else false.
13481 isExpanded : function(){
13482 return this.list.isVisible();
13486 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13487 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13488 * @param {String} value The data value of the item to select
13489 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13490 * selected item if it is not currently in view (defaults to true)
13491 * @return {Boolean} True if the value matched an item in the list, else false
13493 selectByValue : function(v, scrollIntoView){
13494 if(v !== undefined && v !== null){
13495 var r = this.findRecord(this.valueField || this.displayField, v);
13497 this.select(this.store.indexOf(r), scrollIntoView);
13505 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13506 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13507 * @param {Number} index The zero-based index of the list item to select
13508 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13509 * selected item if it is not currently in view (defaults to true)
13511 select : function(index, scrollIntoView){
13512 this.selectedIndex = index;
13513 this.view.select(index);
13514 if(scrollIntoView !== false){
13515 var el = this.view.getNode(index);
13517 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13520 this.list.scrollChildIntoView(el, false);
13526 selectNext : function(){
13527 var ct = this.store.getCount();
13529 if(this.selectedIndex == -1){
13531 }else if(this.selectedIndex < ct-1){
13532 this.select(this.selectedIndex+1);
13538 selectPrev : function(){
13539 var ct = this.store.getCount();
13541 if(this.selectedIndex == -1){
13543 }else if(this.selectedIndex != 0){
13544 this.select(this.selectedIndex-1);
13550 onKeyUp : function(e){
13551 if(this.editable !== false && !e.isSpecialKey()){
13552 this.lastKey = e.getKey();
13553 this.dqTask.delay(this.queryDelay);
13558 validateBlur : function(){
13559 return !this.list || !this.list.isVisible();
13563 initQuery : function(){
13565 var v = this.getRawValue();
13567 if(this.tickable && this.editable){
13568 v = this.tickableInputEl().getValue();
13575 doForce : function(){
13576 if(this.inputEl().dom.value.length > 0){
13577 this.inputEl().dom.value =
13578 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13584 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13585 * query allowing the query action to be canceled if needed.
13586 * @param {String} query The SQL query to execute
13587 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13588 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13589 * saved in the current store (defaults to false)
13591 doQuery : function(q, forceAll){
13593 if(q === undefined || q === null){
13598 forceAll: forceAll,
13602 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13607 forceAll = qe.forceAll;
13608 if(forceAll === true || (q.length >= this.minChars)){
13610 this.hasQuery = true;
13612 if(this.lastQuery != q || this.alwaysQuery){
13613 this.lastQuery = q;
13614 if(this.mode == 'local'){
13615 this.selectedIndex = -1;
13617 this.store.clearFilter();
13620 if(this.specialFilter){
13621 this.fireEvent('specialfilter', this);
13626 this.store.filter(this.displayField, q);
13629 this.store.fireEvent("datachanged", this.store);
13636 this.store.baseParams[this.queryParam] = q;
13638 var options = {params : this.getParams(q)};
13641 options.add = true;
13642 options.params.start = this.page * this.pageSize;
13645 this.store.load(options);
13648 * this code will make the page width larger, at the beginning, the list not align correctly,
13649 * we should expand the list on onLoad
13650 * so command out it
13655 this.selectedIndex = -1;
13660 this.loadNext = false;
13664 getParams : function(q){
13666 //p[this.queryParam] = q;
13670 p.limit = this.pageSize;
13676 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13678 collapse : function(){
13679 if(!this.isExpanded()){
13685 this.hasFocus = false;
13689 this.cancelBtn.hide();
13690 this.trigger.show();
13693 this.tickableInputEl().dom.value = '';
13694 this.tickableInputEl().blur();
13699 Roo.get(document).un('mousedown', this.collapseIf, this);
13700 Roo.get(document).un('mousewheel', this.collapseIf, this);
13701 if (!this.editable) {
13702 Roo.get(document).un('keydown', this.listKeyPress, this);
13704 this.fireEvent('collapse', this);
13710 collapseIf : function(e){
13711 var in_combo = e.within(this.el);
13712 var in_list = e.within(this.list);
13713 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13715 if (in_combo || in_list || is_list) {
13716 //e.stopPropagation();
13721 this.onTickableFooterButtonClick(e, false, false);
13729 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13731 expand : function(){
13733 if(this.isExpanded() || !this.hasFocus){
13737 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13738 this.list.setWidth(lw);
13744 this.restrictHeight();
13748 this.tickItems = Roo.apply([], this.item);
13751 this.cancelBtn.show();
13752 this.trigger.hide();
13755 this.tickableInputEl().focus();
13760 Roo.get(document).on('mousedown', this.collapseIf, this);
13761 Roo.get(document).on('mousewheel', this.collapseIf, this);
13762 if (!this.editable) {
13763 Roo.get(document).on('keydown', this.listKeyPress, this);
13766 this.fireEvent('expand', this);
13770 // Implements the default empty TriggerField.onTriggerClick function
13771 onTriggerClick : function(e)
13773 Roo.log('trigger click');
13775 if(this.disabled || !this.triggerList){
13780 this.loadNext = false;
13782 if(this.isExpanded()){
13784 if (!this.blockFocus) {
13785 this.inputEl().focus();
13789 this.hasFocus = true;
13790 if(this.triggerAction == 'all') {
13791 this.doQuery(this.allQuery, true);
13793 this.doQuery(this.getRawValue());
13795 if (!this.blockFocus) {
13796 this.inputEl().focus();
13801 onTickableTriggerClick : function(e)
13808 this.loadNext = false;
13809 this.hasFocus = true;
13811 if(this.triggerAction == 'all') {
13812 this.doQuery(this.allQuery, true);
13814 this.doQuery(this.getRawValue());
13818 onSearchFieldClick : function(e)
13820 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13821 this.onTickableFooterButtonClick(e, false, false);
13825 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13830 this.loadNext = false;
13831 this.hasFocus = true;
13833 if(this.triggerAction == 'all') {
13834 this.doQuery(this.allQuery, true);
13836 this.doQuery(this.getRawValue());
13840 listKeyPress : function(e)
13842 //Roo.log('listkeypress');
13843 // scroll to first matching element based on key pres..
13844 if (e.isSpecialKey()) {
13847 var k = String.fromCharCode(e.getKey()).toUpperCase();
13850 var csel = this.view.getSelectedNodes();
13851 var cselitem = false;
13853 var ix = this.view.indexOf(csel[0]);
13854 cselitem = this.store.getAt(ix);
13855 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13861 this.store.each(function(v) {
13863 // start at existing selection.
13864 if (cselitem.id == v.id) {
13870 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13871 match = this.store.indexOf(v);
13877 if (match === false) {
13878 return true; // no more action?
13881 this.view.select(match);
13882 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13883 sn.scrollIntoView(sn.dom.parentNode, false);
13886 onViewScroll : function(e, t){
13888 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){
13892 this.hasQuery = true;
13894 this.loading = this.list.select('.loading', true).first();
13896 if(this.loading === null){
13897 this.list.createChild({
13899 cls: 'loading roo-select2-more-results roo-select2-active',
13900 html: 'Loading more results...'
13903 this.loading = this.list.select('.loading', true).first();
13905 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13907 this.loading.hide();
13910 this.loading.show();
13915 this.loadNext = true;
13917 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13922 addItem : function(o)
13924 var dv = ''; // display value
13926 if (this.displayField) {
13927 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13929 // this is an error condition!!!
13930 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13937 var choice = this.choices.createChild({
13939 cls: 'roo-select2-search-choice',
13948 cls: 'roo-select2-search-choice-close',
13953 }, this.searchField);
13955 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13957 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13965 this.inputEl().dom.value = '';
13970 onRemoveItem : function(e, _self, o)
13972 e.preventDefault();
13974 this.lastItem = Roo.apply([], this.item);
13976 var index = this.item.indexOf(o.data) * 1;
13979 Roo.log('not this item?!');
13983 this.item.splice(index, 1);
13988 this.fireEvent('remove', this, e);
13994 syncValue : function()
13996 if(!this.item.length){
14003 Roo.each(this.item, function(i){
14004 if(_this.valueField){
14005 value.push(i[_this.valueField]);
14012 this.value = value.join(',');
14014 if(this.hiddenField){
14015 this.hiddenField.dom.value = this.value;
14018 this.store.fireEvent("datachanged", this.store);
14023 clearItem : function()
14025 if(!this.multiple){
14031 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14039 if(this.tickable && !Roo.isTouch){
14040 this.view.refresh();
14044 inputEl: function ()
14046 if(Roo.isIOS && this.useNativeIOS){
14047 return this.el.select('select.roo-ios-select', true).first();
14050 if(Roo.isTouch && this.mobileTouchView){
14051 return this.el.select('input.form-control',true).first();
14055 return this.searchField;
14058 return this.el.select('input.form-control',true).first();
14061 onTickableFooterButtonClick : function(e, btn, el)
14063 e.preventDefault();
14065 this.lastItem = Roo.apply([], this.item);
14067 if(btn && btn.name == 'cancel'){
14068 this.tickItems = Roo.apply([], this.item);
14077 Roo.each(this.tickItems, function(o){
14085 validate : function()
14087 var v = this.getRawValue();
14090 v = this.getValue();
14093 if(this.disabled || this.allowBlank || v.length){
14098 this.markInvalid();
14102 tickableInputEl : function()
14104 if(!this.tickable || !this.editable){
14105 return this.inputEl();
14108 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14112 getAutoCreateTouchView : function()
14117 cls: 'form-group' //input-group
14123 type : this.inputType,
14124 cls : 'form-control x-combo-noedit',
14125 autocomplete: 'new-password',
14126 placeholder : this.placeholder || '',
14131 input.name = this.name;
14135 input.cls += ' input-' + this.size;
14138 if (this.disabled) {
14139 input.disabled = true;
14150 inputblock.cls += ' input-group';
14152 inputblock.cn.unshift({
14154 cls : 'input-group-addon',
14159 if(this.removable && !this.multiple){
14160 inputblock.cls += ' roo-removable';
14162 inputblock.cn.push({
14165 cls : 'roo-combo-removable-btn close'
14169 if(this.hasFeedback && !this.allowBlank){
14171 inputblock.cls += ' has-feedback';
14173 inputblock.cn.push({
14175 cls: 'glyphicon form-control-feedback'
14182 inputblock.cls += (this.before) ? '' : ' input-group';
14184 inputblock.cn.push({
14186 cls : 'input-group-addon',
14197 cls: 'form-hidden-field'
14211 cls: 'form-hidden-field'
14215 cls: 'roo-select2-choices',
14219 cls: 'roo-select2-search-field',
14232 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14238 if(!this.multiple && this.showToggleBtn){
14245 if (this.caret != false) {
14248 cls: 'fa fa-' + this.caret
14255 cls : 'input-group-addon btn dropdown-toggle',
14260 cls: 'combobox-clear',
14274 combobox.cls += ' roo-select2-container-multi';
14277 var align = this.labelAlign || this.parentLabelAlign();
14281 if(this.fieldLabel.length && this.labelWidth){
14283 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14284 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14289 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14290 tooltip : 'This field is required'
14294 cls : 'control-label ' + lw,
14295 html : this.fieldLabel
14306 if(this.indicatorpos == 'right'){
14310 cls : 'control-label ' + lw,
14311 html : this.fieldLabel
14316 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14317 tooltip : 'This field is required'
14329 var settings = this;
14331 ['xs','sm','md','lg'].map(function(size){
14332 if (settings[size]) {
14333 cfg.cls += ' col-' + size + '-' + settings[size];
14340 initTouchView : function()
14342 this.renderTouchView();
14344 this.touchViewEl.on('scroll', function(){
14345 this.el.dom.scrollTop = 0;
14348 this.originalValue = this.getValue();
14350 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14352 this.inputEl().on("click", this.showTouchView, this);
14353 if (this.triggerEl) {
14354 this.triggerEl.on("click", this.showTouchView, this);
14358 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14359 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14361 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14363 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14364 this.store.on('load', this.onTouchViewLoad, this);
14365 this.store.on('loadexception', this.onTouchViewLoadException, this);
14367 if(this.hiddenName){
14369 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14371 this.hiddenField.dom.value =
14372 this.hiddenValue !== undefined ? this.hiddenValue :
14373 this.value !== undefined ? this.value : '';
14375 this.el.dom.removeAttribute('name');
14376 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14380 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14381 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14384 if(this.removable && !this.multiple){
14385 var close = this.closeTriggerEl();
14387 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14388 close.on('click', this.removeBtnClick, this, close);
14392 * fix the bug in Safari iOS8
14394 this.inputEl().on("focus", function(e){
14395 document.activeElement.blur();
14403 renderTouchView : function()
14405 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14406 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14408 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14409 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14411 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14412 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14413 this.touchViewBodyEl.setStyle('overflow', 'auto');
14415 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14416 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14418 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14419 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14423 showTouchView : function()
14429 this.touchViewHeaderEl.hide();
14431 if(this.modalTitle.length){
14432 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14433 this.touchViewHeaderEl.show();
14436 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14437 this.touchViewEl.show();
14439 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14440 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14441 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14443 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14445 if(this.modalTitle.length){
14446 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14449 this.touchViewBodyEl.setHeight(bodyHeight);
14453 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14455 this.touchViewEl.addClass('in');
14458 this.doTouchViewQuery();
14462 hideTouchView : function()
14464 this.touchViewEl.removeClass('in');
14468 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14470 this.touchViewEl.setStyle('display', 'none');
14475 setTouchViewValue : function()
14482 Roo.each(this.tickItems, function(o){
14487 this.hideTouchView();
14490 doTouchViewQuery : function()
14499 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14503 if(!this.alwaysQuery || this.mode == 'local'){
14504 this.onTouchViewLoad();
14511 onTouchViewBeforeLoad : function(combo,opts)
14517 onTouchViewLoad : function()
14519 if(this.store.getCount() < 1){
14520 this.onTouchViewEmptyResults();
14524 this.clearTouchView();
14526 var rawValue = this.getRawValue();
14528 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14530 this.tickItems = [];
14532 this.store.data.each(function(d, rowIndex){
14533 var row = this.touchViewListGroup.createChild(template);
14535 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14536 row.addClass(d.data.cls);
14539 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14542 html : d.data[this.displayField]
14545 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14546 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14549 row.removeClass('selected');
14550 if(!this.multiple && this.valueField &&
14551 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14554 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555 row.addClass('selected');
14558 if(this.multiple && this.valueField &&
14559 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14563 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14564 this.tickItems.push(d.data);
14567 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14571 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14573 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14575 if(this.modalTitle.length){
14576 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14579 var listHeight = this.touchViewListGroup.getHeight();
14583 if(firstChecked && listHeight > bodyHeight){
14584 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14589 onTouchViewLoadException : function()
14591 this.hideTouchView();
14594 onTouchViewEmptyResults : function()
14596 this.clearTouchView();
14598 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14600 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14604 clearTouchView : function()
14606 this.touchViewListGroup.dom.innerHTML = '';
14609 onTouchViewClick : function(e, el, o)
14611 e.preventDefault();
14614 var rowIndex = o.rowIndex;
14616 var r = this.store.getAt(rowIndex);
14618 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14620 if(!this.multiple){
14621 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14622 c.dom.removeAttribute('checked');
14625 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14627 this.setFromData(r.data);
14629 var close = this.closeTriggerEl();
14635 this.hideTouchView();
14637 this.fireEvent('select', this, r, rowIndex);
14642 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14643 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14644 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14648 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14649 this.addItem(r.data);
14650 this.tickItems.push(r.data);
14654 getAutoCreateNativeIOS : function()
14657 cls: 'form-group' //input-group,
14662 cls : 'roo-ios-select'
14666 combobox.name = this.name;
14669 if (this.disabled) {
14670 combobox.disabled = true;
14673 var settings = this;
14675 ['xs','sm','md','lg'].map(function(size){
14676 if (settings[size]) {
14677 cfg.cls += ' col-' + size + '-' + settings[size];
14687 initIOSView : function()
14689 this.store.on('load', this.onIOSViewLoad, this);
14694 onIOSViewLoad : function()
14696 if(this.store.getCount() < 1){
14700 this.clearIOSView();
14702 if(this.allowBlank) {
14704 var default_text = '-- SELECT --';
14706 var opt = this.inputEl().createChild({
14709 html : default_text
14713 o[this.valueField] = 0;
14714 o[this.displayField] = default_text;
14716 this.ios_options.push({
14723 this.store.data.each(function(d, rowIndex){
14727 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14728 html = d.data[this.displayField];
14733 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14734 value = d.data[this.valueField];
14743 if(this.value == d.data[this.valueField]){
14744 option['selected'] = true;
14747 var opt = this.inputEl().createChild(option);
14749 this.ios_options.push({
14756 this.inputEl().on('change', function(){
14757 this.fireEvent('select', this);
14762 clearIOSView: function()
14764 this.inputEl().dom.innerHTML = '';
14766 this.ios_options = [];
14769 setIOSValue: function(v)
14773 if(!this.ios_options){
14777 Roo.each(this.ios_options, function(opts){
14779 opts.el.dom.removeAttribute('selected');
14781 if(opts.data[this.valueField] != v){
14785 opts.el.dom.setAttribute('selected', true);
14791 * @cfg {Boolean} grow
14795 * @cfg {Number} growMin
14799 * @cfg {Number} growMax
14808 Roo.apply(Roo.bootstrap.ComboBox, {
14812 cls: 'modal-header',
14834 cls: 'list-group-item',
14838 cls: 'roo-combobox-list-group-item-value'
14842 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14856 listItemCheckbox : {
14858 cls: 'list-group-item',
14862 cls: 'roo-combobox-list-group-item-value'
14866 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14882 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14887 cls: 'modal-footer',
14895 cls: 'col-xs-6 text-left',
14898 cls: 'btn btn-danger roo-touch-view-cancel',
14904 cls: 'col-xs-6 text-right',
14907 cls: 'btn btn-success roo-touch-view-ok',
14918 Roo.apply(Roo.bootstrap.ComboBox, {
14920 touchViewTemplate : {
14922 cls: 'modal fade roo-combobox-touch-view',
14926 cls: 'modal-dialog',
14927 style : 'position:fixed', // we have to fix position....
14931 cls: 'modal-content',
14933 Roo.bootstrap.ComboBox.header,
14934 Roo.bootstrap.ComboBox.body,
14935 Roo.bootstrap.ComboBox.footer
14944 * Ext JS Library 1.1.1
14945 * Copyright(c) 2006-2007, Ext JS, LLC.
14947 * Originally Released Under LGPL - original licence link has changed is not relivant.
14950 * <script type="text/javascript">
14955 * @extends Roo.util.Observable
14956 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14957 * This class also supports single and multi selection modes. <br>
14958 * Create a data model bound view:
14960 var store = new Roo.data.Store(...);
14962 var view = new Roo.View({
14964 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14966 singleSelect: true,
14967 selectedClass: "ydataview-selected",
14971 // listen for node click?
14972 view.on("click", function(vw, index, node, e){
14973 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14977 dataModel.load("foobar.xml");
14979 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14981 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14982 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14984 * Note: old style constructor is still suported (container, template, config)
14987 * Create a new View
14988 * @param {Object} config The config object
14991 Roo.View = function(config, depreciated_tpl, depreciated_config){
14993 this.parent = false;
14995 if (typeof(depreciated_tpl) == 'undefined') {
14996 // new way.. - universal constructor.
14997 Roo.apply(this, config);
14998 this.el = Roo.get(this.el);
15001 this.el = Roo.get(config);
15002 this.tpl = depreciated_tpl;
15003 Roo.apply(this, depreciated_config);
15005 this.wrapEl = this.el.wrap().wrap();
15006 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15009 if(typeof(this.tpl) == "string"){
15010 this.tpl = new Roo.Template(this.tpl);
15012 // support xtype ctors..
15013 this.tpl = new Roo.factory(this.tpl, Roo);
15017 this.tpl.compile();
15022 * @event beforeclick
15023 * Fires before a click is processed. Returns false to cancel the default action.
15024 * @param {Roo.View} this
15025 * @param {Number} index The index of the target node
15026 * @param {HTMLElement} node The target node
15027 * @param {Roo.EventObject} e The raw event object
15029 "beforeclick" : true,
15032 * Fires when a template node is clicked.
15033 * @param {Roo.View} this
15034 * @param {Number} index The index of the target node
15035 * @param {HTMLElement} node The target node
15036 * @param {Roo.EventObject} e The raw event object
15041 * Fires when a template node is double clicked.
15042 * @param {Roo.View} this
15043 * @param {Number} index The index of the target node
15044 * @param {HTMLElement} node The target node
15045 * @param {Roo.EventObject} e The raw event object
15049 * @event contextmenu
15050 * Fires when a template node is right clicked.
15051 * @param {Roo.View} this
15052 * @param {Number} index The index of the target node
15053 * @param {HTMLElement} node The target node
15054 * @param {Roo.EventObject} e The raw event object
15056 "contextmenu" : true,
15058 * @event selectionchange
15059 * Fires when the selected nodes change.
15060 * @param {Roo.View} this
15061 * @param {Array} selections Array of the selected nodes
15063 "selectionchange" : true,
15066 * @event beforeselect
15067 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15068 * @param {Roo.View} this
15069 * @param {HTMLElement} node The node to be selected
15070 * @param {Array} selections Array of currently selected nodes
15072 "beforeselect" : true,
15074 * @event preparedata
15075 * Fires on every row to render, to allow you to change the data.
15076 * @param {Roo.View} this
15077 * @param {Object} data to be rendered (change this)
15079 "preparedata" : true
15087 "click": this.onClick,
15088 "dblclick": this.onDblClick,
15089 "contextmenu": this.onContextMenu,
15093 this.selections = [];
15095 this.cmp = new Roo.CompositeElementLite([]);
15097 this.store = Roo.factory(this.store, Roo.data);
15098 this.setStore(this.store, true);
15101 if ( this.footer && this.footer.xtype) {
15103 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15105 this.footer.dataSource = this.store;
15106 this.footer.container = fctr;
15107 this.footer = Roo.factory(this.footer, Roo);
15108 fctr.insertFirst(this.el);
15110 // this is a bit insane - as the paging toolbar seems to detach the el..
15111 // dom.parentNode.parentNode.parentNode
15112 // they get detached?
15116 Roo.View.superclass.constructor.call(this);
15121 Roo.extend(Roo.View, Roo.util.Observable, {
15124 * @cfg {Roo.data.Store} store Data store to load data from.
15129 * @cfg {String|Roo.Element} el The container element.
15134 * @cfg {String|Roo.Template} tpl The template used by this View
15138 * @cfg {String} dataName the named area of the template to use as the data area
15139 * Works with domtemplates roo-name="name"
15143 * @cfg {String} selectedClass The css class to add to selected nodes
15145 selectedClass : "x-view-selected",
15147 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15152 * @cfg {String} text to display on mask (default Loading)
15156 * @cfg {Boolean} multiSelect Allow multiple selection
15158 multiSelect : false,
15160 * @cfg {Boolean} singleSelect Allow single selection
15162 singleSelect: false,
15165 * @cfg {Boolean} toggleSelect - selecting
15167 toggleSelect : false,
15170 * @cfg {Boolean} tickable - selecting
15175 * Returns the element this view is bound to.
15176 * @return {Roo.Element}
15178 getEl : function(){
15179 return this.wrapEl;
15185 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15187 refresh : function(){
15188 //Roo.log('refresh');
15191 // if we are using something like 'domtemplate', then
15192 // the what gets used is:
15193 // t.applySubtemplate(NAME, data, wrapping data..)
15194 // the outer template then get' applied with
15195 // the store 'extra data'
15196 // and the body get's added to the
15197 // roo-name="data" node?
15198 // <span class='roo-tpl-{name}'></span> ?????
15202 this.clearSelections();
15203 this.el.update("");
15205 var records = this.store.getRange();
15206 if(records.length < 1) {
15208 // is this valid?? = should it render a template??
15210 this.el.update(this.emptyText);
15214 if (this.dataName) {
15215 this.el.update(t.apply(this.store.meta)); //????
15216 el = this.el.child('.roo-tpl-' + this.dataName);
15219 for(var i = 0, len = records.length; i < len; i++){
15220 var data = this.prepareData(records[i].data, i, records[i]);
15221 this.fireEvent("preparedata", this, data, i, records[i]);
15223 var d = Roo.apply({}, data);
15226 Roo.apply(d, {'roo-id' : Roo.id()});
15230 Roo.each(this.parent.item, function(item){
15231 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15234 Roo.apply(d, {'roo-data-checked' : 'checked'});
15238 html[html.length] = Roo.util.Format.trim(
15240 t.applySubtemplate(this.dataName, d, this.store.meta) :
15247 el.update(html.join(""));
15248 this.nodes = el.dom.childNodes;
15249 this.updateIndexes(0);
15254 * Function to override to reformat the data that is sent to
15255 * the template for each node.
15256 * DEPRICATED - use the preparedata event handler.
15257 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15258 * a JSON object for an UpdateManager bound view).
15260 prepareData : function(data, index, record)
15262 this.fireEvent("preparedata", this, data, index, record);
15266 onUpdate : function(ds, record){
15267 // Roo.log('on update');
15268 this.clearSelections();
15269 var index = this.store.indexOf(record);
15270 var n = this.nodes[index];
15271 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15272 n.parentNode.removeChild(n);
15273 this.updateIndexes(index, index);
15279 onAdd : function(ds, records, index)
15281 //Roo.log(['on Add', ds, records, index] );
15282 this.clearSelections();
15283 if(this.nodes.length == 0){
15287 var n = this.nodes[index];
15288 for(var i = 0, len = records.length; i < len; i++){
15289 var d = this.prepareData(records[i].data, i, records[i]);
15291 this.tpl.insertBefore(n, d);
15294 this.tpl.append(this.el, d);
15297 this.updateIndexes(index);
15300 onRemove : function(ds, record, index){
15301 // Roo.log('onRemove');
15302 this.clearSelections();
15303 var el = this.dataName ?
15304 this.el.child('.roo-tpl-' + this.dataName) :
15307 el.dom.removeChild(this.nodes[index]);
15308 this.updateIndexes(index);
15312 * Refresh an individual node.
15313 * @param {Number} index
15315 refreshNode : function(index){
15316 this.onUpdate(this.store, this.store.getAt(index));
15319 updateIndexes : function(startIndex, endIndex){
15320 var ns = this.nodes;
15321 startIndex = startIndex || 0;
15322 endIndex = endIndex || ns.length - 1;
15323 for(var i = startIndex; i <= endIndex; i++){
15324 ns[i].nodeIndex = i;
15329 * Changes the data store this view uses and refresh the view.
15330 * @param {Store} store
15332 setStore : function(store, initial){
15333 if(!initial && this.store){
15334 this.store.un("datachanged", this.refresh);
15335 this.store.un("add", this.onAdd);
15336 this.store.un("remove", this.onRemove);
15337 this.store.un("update", this.onUpdate);
15338 this.store.un("clear", this.refresh);
15339 this.store.un("beforeload", this.onBeforeLoad);
15340 this.store.un("load", this.onLoad);
15341 this.store.un("loadexception", this.onLoad);
15345 store.on("datachanged", this.refresh, this);
15346 store.on("add", this.onAdd, this);
15347 store.on("remove", this.onRemove, this);
15348 store.on("update", this.onUpdate, this);
15349 store.on("clear", this.refresh, this);
15350 store.on("beforeload", this.onBeforeLoad, this);
15351 store.on("load", this.onLoad, this);
15352 store.on("loadexception", this.onLoad, this);
15360 * onbeforeLoad - masks the loading area.
15363 onBeforeLoad : function(store,opts)
15365 //Roo.log('onBeforeLoad');
15367 this.el.update("");
15369 this.el.mask(this.mask ? this.mask : "Loading" );
15371 onLoad : function ()
15378 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15379 * @param {HTMLElement} node
15380 * @return {HTMLElement} The template node
15382 findItemFromChild : function(node){
15383 var el = this.dataName ?
15384 this.el.child('.roo-tpl-' + this.dataName,true) :
15387 if(!node || node.parentNode == el){
15390 var p = node.parentNode;
15391 while(p && p != el){
15392 if(p.parentNode == el){
15401 onClick : function(e){
15402 var item = this.findItemFromChild(e.getTarget());
15404 var index = this.indexOf(item);
15405 if(this.onItemClick(item, index, e) !== false){
15406 this.fireEvent("click", this, index, item, e);
15409 this.clearSelections();
15414 onContextMenu : function(e){
15415 var item = this.findItemFromChild(e.getTarget());
15417 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15422 onDblClick : function(e){
15423 var item = this.findItemFromChild(e.getTarget());
15425 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15429 onItemClick : function(item, index, e)
15431 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15434 if (this.toggleSelect) {
15435 var m = this.isSelected(item) ? 'unselect' : 'select';
15438 _t[m](item, true, false);
15441 if(this.multiSelect || this.singleSelect){
15442 if(this.multiSelect && e.shiftKey && this.lastSelection){
15443 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15445 this.select(item, this.multiSelect && e.ctrlKey);
15446 this.lastSelection = item;
15449 if(!this.tickable){
15450 e.preventDefault();
15458 * Get the number of selected nodes.
15461 getSelectionCount : function(){
15462 return this.selections.length;
15466 * Get the currently selected nodes.
15467 * @return {Array} An array of HTMLElements
15469 getSelectedNodes : function(){
15470 return this.selections;
15474 * Get the indexes of the selected nodes.
15477 getSelectedIndexes : function(){
15478 var indexes = [], s = this.selections;
15479 for(var i = 0, len = s.length; i < len; i++){
15480 indexes.push(s[i].nodeIndex);
15486 * Clear all selections
15487 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15489 clearSelections : function(suppressEvent){
15490 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15491 this.cmp.elements = this.selections;
15492 this.cmp.removeClass(this.selectedClass);
15493 this.selections = [];
15494 if(!suppressEvent){
15495 this.fireEvent("selectionchange", this, this.selections);
15501 * Returns true if the passed node is selected
15502 * @param {HTMLElement/Number} node The node or node index
15503 * @return {Boolean}
15505 isSelected : function(node){
15506 var s = this.selections;
15510 node = this.getNode(node);
15511 return s.indexOf(node) !== -1;
15516 * @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
15517 * @param {Boolean} keepExisting (optional) true to keep existing selections
15518 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15520 select : function(nodeInfo, keepExisting, suppressEvent){
15521 if(nodeInfo instanceof Array){
15523 this.clearSelections(true);
15525 for(var i = 0, len = nodeInfo.length; i < len; i++){
15526 this.select(nodeInfo[i], true, true);
15530 var node = this.getNode(nodeInfo);
15531 if(!node || this.isSelected(node)){
15532 return; // already selected.
15535 this.clearSelections(true);
15538 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15539 Roo.fly(node).addClass(this.selectedClass);
15540 this.selections.push(node);
15541 if(!suppressEvent){
15542 this.fireEvent("selectionchange", this, this.selections);
15550 * @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
15551 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15552 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15554 unselect : function(nodeInfo, keepExisting, suppressEvent)
15556 if(nodeInfo instanceof Array){
15557 Roo.each(this.selections, function(s) {
15558 this.unselect(s, nodeInfo);
15562 var node = this.getNode(nodeInfo);
15563 if(!node || !this.isSelected(node)){
15564 //Roo.log("not selected");
15565 return; // not selected.
15569 Roo.each(this.selections, function(s) {
15571 Roo.fly(node).removeClass(this.selectedClass);
15578 this.selections= ns;
15579 this.fireEvent("selectionchange", this, this.selections);
15583 * Gets a template node.
15584 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15585 * @return {HTMLElement} The node or null if it wasn't found
15587 getNode : function(nodeInfo){
15588 if(typeof nodeInfo == "string"){
15589 return document.getElementById(nodeInfo);
15590 }else if(typeof nodeInfo == "number"){
15591 return this.nodes[nodeInfo];
15597 * Gets a range template nodes.
15598 * @param {Number} startIndex
15599 * @param {Number} endIndex
15600 * @return {Array} An array of nodes
15602 getNodes : function(start, end){
15603 var ns = this.nodes;
15604 start = start || 0;
15605 end = typeof end == "undefined" ? ns.length - 1 : end;
15608 for(var i = start; i <= end; i++){
15612 for(var i = start; i >= end; i--){
15620 * Finds the index of the passed node
15621 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15622 * @return {Number} The index of the node or -1
15624 indexOf : function(node){
15625 node = this.getNode(node);
15626 if(typeof node.nodeIndex == "number"){
15627 return node.nodeIndex;
15629 var ns = this.nodes;
15630 for(var i = 0, len = ns.length; i < len; i++){
15641 * based on jquery fullcalendar
15645 Roo.bootstrap = Roo.bootstrap || {};
15647 * @class Roo.bootstrap.Calendar
15648 * @extends Roo.bootstrap.Component
15649 * Bootstrap Calendar class
15650 * @cfg {Boolean} loadMask (true|false) default false
15651 * @cfg {Object} header generate the user specific header of the calendar, default false
15654 * Create a new Container
15655 * @param {Object} config The config object
15660 Roo.bootstrap.Calendar = function(config){
15661 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15665 * Fires when a date is selected
15666 * @param {DatePicker} this
15667 * @param {Date} date The selected date
15671 * @event monthchange
15672 * Fires when the displayed month changes
15673 * @param {DatePicker} this
15674 * @param {Date} date The selected month
15676 'monthchange': true,
15678 * @event evententer
15679 * Fires when mouse over an event
15680 * @param {Calendar} this
15681 * @param {event} Event
15683 'evententer': true,
15685 * @event eventleave
15686 * Fires when the mouse leaves an
15687 * @param {Calendar} this
15690 'eventleave': true,
15692 * @event eventclick
15693 * Fires when the mouse click an
15694 * @param {Calendar} this
15703 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15706 * @cfg {Number} startDay
15707 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15715 getAutoCreate : function(){
15718 var fc_button = function(name, corner, style, content ) {
15719 return Roo.apply({},{
15721 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15723 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15726 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15737 style : 'width:100%',
15744 cls : 'fc-header-left',
15746 fc_button('prev', 'left', 'arrow', '‹' ),
15747 fc_button('next', 'right', 'arrow', '›' ),
15748 { tag: 'span', cls: 'fc-header-space' },
15749 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15757 cls : 'fc-header-center',
15761 cls: 'fc-header-title',
15764 html : 'month / year'
15772 cls : 'fc-header-right',
15774 /* fc_button('month', 'left', '', 'month' ),
15775 fc_button('week', '', '', 'week' ),
15776 fc_button('day', 'right', '', 'day' )
15788 header = this.header;
15791 var cal_heads = function() {
15793 // fixme - handle this.
15795 for (var i =0; i < Date.dayNames.length; i++) {
15796 var d = Date.dayNames[i];
15799 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15800 html : d.substring(0,3)
15804 ret[0].cls += ' fc-first';
15805 ret[6].cls += ' fc-last';
15808 var cal_cell = function(n) {
15811 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15816 cls: 'fc-day-number',
15820 cls: 'fc-day-content',
15824 style: 'position: relative;' // height: 17px;
15836 var cal_rows = function() {
15839 for (var r = 0; r < 6; r++) {
15846 for (var i =0; i < Date.dayNames.length; i++) {
15847 var d = Date.dayNames[i];
15848 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15851 row.cn[0].cls+=' fc-first';
15852 row.cn[0].cn[0].style = 'min-height:90px';
15853 row.cn[6].cls+=' fc-last';
15857 ret[0].cls += ' fc-first';
15858 ret[4].cls += ' fc-prev-last';
15859 ret[5].cls += ' fc-last';
15866 cls: 'fc-border-separate',
15867 style : 'width:100%',
15875 cls : 'fc-first fc-last',
15893 cls : 'fc-content',
15894 style : "position: relative;",
15897 cls : 'fc-view fc-view-month fc-grid',
15898 style : 'position: relative',
15899 unselectable : 'on',
15902 cls : 'fc-event-container',
15903 style : 'position:absolute;z-index:8;top:0;left:0;'
15921 initEvents : function()
15924 throw "can not find store for calendar";
15930 style: "text-align:center",
15934 style: "background-color:white;width:50%;margin:250 auto",
15938 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15949 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15951 var size = this.el.select('.fc-content', true).first().getSize();
15952 this.maskEl.setSize(size.width, size.height);
15953 this.maskEl.enableDisplayMode("block");
15954 if(!this.loadMask){
15955 this.maskEl.hide();
15958 this.store = Roo.factory(this.store, Roo.data);
15959 this.store.on('load', this.onLoad, this);
15960 this.store.on('beforeload', this.onBeforeLoad, this);
15964 this.cells = this.el.select('.fc-day',true);
15965 //Roo.log(this.cells);
15966 this.textNodes = this.el.query('.fc-day-number');
15967 this.cells.addClassOnOver('fc-state-hover');
15969 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15970 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15971 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15972 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15974 this.on('monthchange', this.onMonthChange, this);
15976 this.update(new Date().clearTime());
15979 resize : function() {
15980 var sz = this.el.getSize();
15982 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15983 this.el.select('.fc-day-content div',true).setHeight(34);
15988 showPrevMonth : function(e){
15989 this.update(this.activeDate.add("mo", -1));
15991 showToday : function(e){
15992 this.update(new Date().clearTime());
15995 showNextMonth : function(e){
15996 this.update(this.activeDate.add("mo", 1));
16000 showPrevYear : function(){
16001 this.update(this.activeDate.add("y", -1));
16005 showNextYear : function(){
16006 this.update(this.activeDate.add("y", 1));
16011 update : function(date)
16013 var vd = this.activeDate;
16014 this.activeDate = date;
16015 // if(vd && this.el){
16016 // var t = date.getTime();
16017 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16018 // Roo.log('using add remove');
16020 // this.fireEvent('monthchange', this, date);
16022 // this.cells.removeClass("fc-state-highlight");
16023 // this.cells.each(function(c){
16024 // if(c.dateValue == t){
16025 // c.addClass("fc-state-highlight");
16026 // setTimeout(function(){
16027 // try{c.dom.firstChild.focus();}catch(e){}
16037 var days = date.getDaysInMonth();
16039 var firstOfMonth = date.getFirstDateOfMonth();
16040 var startingPos = firstOfMonth.getDay()-this.startDay;
16042 if(startingPos < this.startDay){
16046 var pm = date.add(Date.MONTH, -1);
16047 var prevStart = pm.getDaysInMonth()-startingPos;
16049 this.cells = this.el.select('.fc-day',true);
16050 this.textNodes = this.el.query('.fc-day-number');
16051 this.cells.addClassOnOver('fc-state-hover');
16053 var cells = this.cells.elements;
16054 var textEls = this.textNodes;
16056 Roo.each(cells, function(cell){
16057 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16060 days += startingPos;
16062 // convert everything to numbers so it's fast
16063 var day = 86400000;
16064 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16067 //Roo.log(prevStart);
16069 var today = new Date().clearTime().getTime();
16070 var sel = date.clearTime().getTime();
16071 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16072 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16073 var ddMatch = this.disabledDatesRE;
16074 var ddText = this.disabledDatesText;
16075 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16076 var ddaysText = this.disabledDaysText;
16077 var format = this.format;
16079 var setCellClass = function(cal, cell){
16083 //Roo.log('set Cell Class');
16085 var t = d.getTime();
16089 cell.dateValue = t;
16091 cell.className += " fc-today";
16092 cell.className += " fc-state-highlight";
16093 cell.title = cal.todayText;
16096 // disable highlight in other month..
16097 //cell.className += " fc-state-highlight";
16102 cell.className = " fc-state-disabled";
16103 cell.title = cal.minText;
16107 cell.className = " fc-state-disabled";
16108 cell.title = cal.maxText;
16112 if(ddays.indexOf(d.getDay()) != -1){
16113 cell.title = ddaysText;
16114 cell.className = " fc-state-disabled";
16117 if(ddMatch && format){
16118 var fvalue = d.dateFormat(format);
16119 if(ddMatch.test(fvalue)){
16120 cell.title = ddText.replace("%0", fvalue);
16121 cell.className = " fc-state-disabled";
16125 if (!cell.initialClassName) {
16126 cell.initialClassName = cell.dom.className;
16129 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16134 for(; i < startingPos; i++) {
16135 textEls[i].innerHTML = (++prevStart);
16136 d.setDate(d.getDate()+1);
16138 cells[i].className = "fc-past fc-other-month";
16139 setCellClass(this, cells[i]);
16144 for(; i < days; i++){
16145 intDay = i - startingPos + 1;
16146 textEls[i].innerHTML = (intDay);
16147 d.setDate(d.getDate()+1);
16149 cells[i].className = ''; // "x-date-active";
16150 setCellClass(this, cells[i]);
16154 for(; i < 42; i++) {
16155 textEls[i].innerHTML = (++extraDays);
16156 d.setDate(d.getDate()+1);
16158 cells[i].className = "fc-future fc-other-month";
16159 setCellClass(this, cells[i]);
16162 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16164 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16166 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16167 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16169 if(totalRows != 6){
16170 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16171 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16174 this.fireEvent('monthchange', this, date);
16178 if(!this.internalRender){
16179 var main = this.el.dom.firstChild;
16180 var w = main.offsetWidth;
16181 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16182 Roo.fly(main).setWidth(w);
16183 this.internalRender = true;
16184 // opera does not respect the auto grow header center column
16185 // then, after it gets a width opera refuses to recalculate
16186 // without a second pass
16187 if(Roo.isOpera && !this.secondPass){
16188 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16189 this.secondPass = true;
16190 this.update.defer(10, this, [date]);
16197 findCell : function(dt) {
16198 dt = dt.clearTime().getTime();
16200 this.cells.each(function(c){
16201 //Roo.log("check " +c.dateValue + '?=' + dt);
16202 if(c.dateValue == dt){
16212 findCells : function(ev) {
16213 var s = ev.start.clone().clearTime().getTime();
16215 var e= ev.end.clone().clearTime().getTime();
16218 this.cells.each(function(c){
16219 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16221 if(c.dateValue > e){
16224 if(c.dateValue < s){
16233 // findBestRow: function(cells)
16237 // for (var i =0 ; i < cells.length;i++) {
16238 // ret = Math.max(cells[i].rows || 0,ret);
16245 addItem : function(ev)
16247 // look for vertical location slot in
16248 var cells = this.findCells(ev);
16250 // ev.row = this.findBestRow(cells);
16252 // work out the location.
16256 for(var i =0; i < cells.length; i++) {
16258 cells[i].row = cells[0].row;
16261 cells[i].row = cells[i].row + 1;
16271 if (crow.start.getY() == cells[i].getY()) {
16273 crow.end = cells[i];
16290 cells[0].events.push(ev);
16292 this.calevents.push(ev);
16295 clearEvents: function() {
16297 if(!this.calevents){
16301 Roo.each(this.cells.elements, function(c){
16307 Roo.each(this.calevents, function(e) {
16308 Roo.each(e.els, function(el) {
16309 el.un('mouseenter' ,this.onEventEnter, this);
16310 el.un('mouseleave' ,this.onEventLeave, this);
16315 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16321 renderEvents: function()
16325 this.cells.each(function(c) {
16334 if(c.row != c.events.length){
16335 r = 4 - (4 - (c.row - c.events.length));
16338 c.events = ev.slice(0, r);
16339 c.more = ev.slice(r);
16341 if(c.more.length && c.more.length == 1){
16342 c.events.push(c.more.pop());
16345 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16349 this.cells.each(function(c) {
16351 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16354 for (var e = 0; e < c.events.length; e++){
16355 var ev = c.events[e];
16356 var rows = ev.rows;
16358 for(var i = 0; i < rows.length; i++) {
16360 // how many rows should it span..
16363 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16364 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16366 unselectable : "on",
16369 cls: 'fc-event-inner',
16373 // cls: 'fc-event-time',
16374 // html : cells.length > 1 ? '' : ev.time
16378 cls: 'fc-event-title',
16379 html : String.format('{0}', ev.title)
16386 cls: 'ui-resizable-handle ui-resizable-e',
16387 html : '  '
16394 cfg.cls += ' fc-event-start';
16396 if ((i+1) == rows.length) {
16397 cfg.cls += ' fc-event-end';
16400 var ctr = _this.el.select('.fc-event-container',true).first();
16401 var cg = ctr.createChild(cfg);
16403 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16404 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16406 var r = (c.more.length) ? 1 : 0;
16407 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16408 cg.setWidth(ebox.right - sbox.x -2);
16410 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16411 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16412 cg.on('click', _this.onEventClick, _this, ev);
16423 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16424 style : 'position: absolute',
16425 unselectable : "on",
16428 cls: 'fc-event-inner',
16432 cls: 'fc-event-title',
16440 cls: 'ui-resizable-handle ui-resizable-e',
16441 html : '  '
16447 var ctr = _this.el.select('.fc-event-container',true).first();
16448 var cg = ctr.createChild(cfg);
16450 var sbox = c.select('.fc-day-content',true).first().getBox();
16451 var ebox = c.select('.fc-day-content',true).first().getBox();
16453 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16454 cg.setWidth(ebox.right - sbox.x -2);
16456 cg.on('click', _this.onMoreEventClick, _this, c.more);
16466 onEventEnter: function (e, el,event,d) {
16467 this.fireEvent('evententer', this, el, event);
16470 onEventLeave: function (e, el,event,d) {
16471 this.fireEvent('eventleave', this, el, event);
16474 onEventClick: function (e, el,event,d) {
16475 this.fireEvent('eventclick', this, el, event);
16478 onMonthChange: function () {
16482 onMoreEventClick: function(e, el, more)
16486 this.calpopover.placement = 'right';
16487 this.calpopover.setTitle('More');
16489 this.calpopover.setContent('');
16491 var ctr = this.calpopover.el.select('.popover-content', true).first();
16493 Roo.each(more, function(m){
16495 cls : 'fc-event-hori fc-event-draggable',
16498 var cg = ctr.createChild(cfg);
16500 cg.on('click', _this.onEventClick, _this, m);
16503 this.calpopover.show(el);
16508 onLoad: function ()
16510 this.calevents = [];
16513 if(this.store.getCount() > 0){
16514 this.store.data.each(function(d){
16517 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16518 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16519 time : d.data.start_time,
16520 title : d.data.title,
16521 description : d.data.description,
16522 venue : d.data.venue
16527 this.renderEvents();
16529 if(this.calevents.length && this.loadMask){
16530 this.maskEl.hide();
16534 onBeforeLoad: function()
16536 this.clearEvents();
16538 this.maskEl.show();
16552 * @class Roo.bootstrap.Popover
16553 * @extends Roo.bootstrap.Component
16554 * Bootstrap Popover class
16555 * @cfg {String} html contents of the popover (or false to use children..)
16556 * @cfg {String} title of popover (or false to hide)
16557 * @cfg {String} placement how it is placed
16558 * @cfg {String} trigger click || hover (or false to trigger manually)
16559 * @cfg {String} over what (parent or false to trigger manually.)
16560 * @cfg {Number} delay - delay before showing
16563 * Create a new Popover
16564 * @param {Object} config The config object
16567 Roo.bootstrap.Popover = function(config){
16568 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16574 * After the popover show
16576 * @param {Roo.bootstrap.Popover} this
16581 * After the popover hide
16583 * @param {Roo.bootstrap.Popover} this
16589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16591 title: 'Fill in a title',
16594 placement : 'right',
16595 trigger : 'hover', // hover
16601 can_build_overlaid : false,
16603 getChildContainer : function()
16605 return this.el.select('.popover-content',true).first();
16608 getAutoCreate : function(){
16611 cls : 'popover roo-dynamic',
16612 style: 'display:block',
16618 cls : 'popover-inner',
16622 cls: 'popover-title',
16626 cls : 'popover-content',
16637 setTitle: function(str)
16640 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16642 setContent: function(str)
16645 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16647 // as it get's added to the bottom of the page.
16648 onRender : function(ct, position)
16650 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16652 var cfg = Roo.apply({}, this.getAutoCreate());
16656 cfg.cls += ' ' + this.cls;
16659 cfg.style = this.style;
16661 //Roo.log("adding to ");
16662 this.el = Roo.get(document.body).createChild(cfg, position);
16663 // Roo.log(this.el);
16668 initEvents : function()
16670 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16671 this.el.enableDisplayMode('block');
16673 if (this.over === false) {
16676 if (this.triggers === false) {
16679 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16680 var triggers = this.trigger ? this.trigger.split(' ') : [];
16681 Roo.each(triggers, function(trigger) {
16683 if (trigger == 'click') {
16684 on_el.on('click', this.toggle, this);
16685 } else if (trigger != 'manual') {
16686 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16687 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16689 on_el.on(eventIn ,this.enter, this);
16690 on_el.on(eventOut, this.leave, this);
16701 toggle : function () {
16702 this.hoverState == 'in' ? this.leave() : this.enter();
16705 enter : function () {
16707 clearTimeout(this.timeout);
16709 this.hoverState = 'in';
16711 if (!this.delay || !this.delay.show) {
16716 this.timeout = setTimeout(function () {
16717 if (_t.hoverState == 'in') {
16720 }, this.delay.show)
16723 leave : function() {
16724 clearTimeout(this.timeout);
16726 this.hoverState = 'out';
16728 if (!this.delay || !this.delay.hide) {
16733 this.timeout = setTimeout(function () {
16734 if (_t.hoverState == 'out') {
16737 }, this.delay.hide)
16740 show : function (on_el)
16743 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16747 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16748 if (this.html !== false) {
16749 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16751 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16752 if (!this.title.length) {
16753 this.el.select('.popover-title',true).hide();
16756 var placement = typeof this.placement == 'function' ?
16757 this.placement.call(this, this.el, on_el) :
16760 var autoToken = /\s?auto?\s?/i;
16761 var autoPlace = autoToken.test(placement);
16763 placement = placement.replace(autoToken, '') || 'top';
16767 //this.el.setXY([0,0]);
16769 this.el.dom.style.display='block';
16770 this.el.addClass(placement);
16772 //this.el.appendTo(on_el);
16774 var p = this.getPosition();
16775 var box = this.el.getBox();
16780 var align = Roo.bootstrap.Popover.alignment[placement];
16781 this.el.alignTo(on_el, align[0],align[1]);
16782 //var arrow = this.el.select('.arrow',true).first();
16783 //arrow.set(align[2],
16785 this.el.addClass('in');
16788 if (this.el.hasClass('fade')) {
16792 this.hoverState = 'in';
16794 this.fireEvent('show', this);
16799 this.el.setXY([0,0]);
16800 this.el.removeClass('in');
16802 this.hoverState = null;
16804 this.fireEvent('hide', this);
16809 Roo.bootstrap.Popover.alignment = {
16810 'left' : ['r-l', [-10,0], 'right'],
16811 'right' : ['l-r', [10,0], 'left'],
16812 'bottom' : ['t-b', [0,10], 'top'],
16813 'top' : [ 'b-t', [0,-10], 'bottom']
16824 * @class Roo.bootstrap.Progress
16825 * @extends Roo.bootstrap.Component
16826 * Bootstrap Progress class
16827 * @cfg {Boolean} striped striped of the progress bar
16828 * @cfg {Boolean} active animated of the progress bar
16832 * Create a new Progress
16833 * @param {Object} config The config object
16836 Roo.bootstrap.Progress = function(config){
16837 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16840 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16845 getAutoCreate : function(){
16853 cfg.cls += ' progress-striped';
16857 cfg.cls += ' active';
16876 * @class Roo.bootstrap.ProgressBar
16877 * @extends Roo.bootstrap.Component
16878 * Bootstrap ProgressBar class
16879 * @cfg {Number} aria_valuenow aria-value now
16880 * @cfg {Number} aria_valuemin aria-value min
16881 * @cfg {Number} aria_valuemax aria-value max
16882 * @cfg {String} label label for the progress bar
16883 * @cfg {String} panel (success | info | warning | danger )
16884 * @cfg {String} role role of the progress bar
16885 * @cfg {String} sr_only text
16889 * Create a new ProgressBar
16890 * @param {Object} config The config object
16893 Roo.bootstrap.ProgressBar = function(config){
16894 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16897 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16901 aria_valuemax : 100,
16907 getAutoCreate : function()
16912 cls: 'progress-bar',
16913 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16925 cfg.role = this.role;
16928 if(this.aria_valuenow){
16929 cfg['aria-valuenow'] = this.aria_valuenow;
16932 if(this.aria_valuemin){
16933 cfg['aria-valuemin'] = this.aria_valuemin;
16936 if(this.aria_valuemax){
16937 cfg['aria-valuemax'] = this.aria_valuemax;
16940 if(this.label && !this.sr_only){
16941 cfg.html = this.label;
16945 cfg.cls += ' progress-bar-' + this.panel;
16951 update : function(aria_valuenow)
16953 this.aria_valuenow = aria_valuenow;
16955 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16970 * @class Roo.bootstrap.TabGroup
16971 * @extends Roo.bootstrap.Column
16972 * Bootstrap Column class
16973 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16974 * @cfg {Boolean} carousel true to make the group behave like a carousel
16975 * @cfg {Boolean} bullets show bullets for the panels
16976 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16977 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16978 * @cfg {Boolean} showarrow (true|false) show arrow default true
16981 * Create a new TabGroup
16982 * @param {Object} config The config object
16985 Roo.bootstrap.TabGroup = function(config){
16986 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16988 this.navId = Roo.id();
16991 Roo.bootstrap.TabGroup.register(this);
16995 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16998 transition : false,
17003 slideOnTouch : false,
17006 getAutoCreate : function()
17008 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17010 cfg.cls += ' tab-content';
17012 if (this.carousel) {
17013 cfg.cls += ' carousel slide';
17016 cls : 'carousel-inner',
17020 if(this.bullets && !Roo.isTouch){
17023 cls : 'carousel-bullets',
17027 if(this.bullets_cls){
17028 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17035 cfg.cn[0].cn.push(bullets);
17038 if(this.showarrow){
17039 cfg.cn[0].cn.push({
17041 class : 'carousel-arrow',
17045 class : 'carousel-prev',
17049 class : 'fa fa-chevron-left'
17055 class : 'carousel-next',
17059 class : 'fa fa-chevron-right'
17072 initEvents: function()
17074 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17075 // this.el.on("touchstart", this.onTouchStart, this);
17078 if(this.autoslide){
17081 this.slideFn = window.setInterval(function() {
17082 _this.showPanelNext();
17086 if(this.showarrow){
17087 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17088 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17094 // onTouchStart : function(e, el, o)
17096 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17100 // this.showPanelNext();
17104 getChildContainer : function()
17106 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17110 * register a Navigation item
17111 * @param {Roo.bootstrap.NavItem} the navitem to add
17113 register : function(item)
17115 this.tabs.push( item);
17116 item.navId = this.navId; // not really needed..
17121 getActivePanel : function()
17124 Roo.each(this.tabs, function(t) {
17134 getPanelByName : function(n)
17137 Roo.each(this.tabs, function(t) {
17138 if (t.tabId == n) {
17146 indexOfPanel : function(p)
17149 Roo.each(this.tabs, function(t,i) {
17150 if (t.tabId == p.tabId) {
17159 * show a specific panel
17160 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17161 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17163 showPanel : function (pan)
17165 if(this.transition || typeof(pan) == 'undefined'){
17166 Roo.log("waiting for the transitionend");
17170 if (typeof(pan) == 'number') {
17171 pan = this.tabs[pan];
17174 if (typeof(pan) == 'string') {
17175 pan = this.getPanelByName(pan);
17178 var cur = this.getActivePanel();
17181 Roo.log('pan or acitve pan is undefined');
17185 if (pan.tabId == this.getActivePanel().tabId) {
17189 if (false === cur.fireEvent('beforedeactivate')) {
17193 if(this.bullets > 0 && !Roo.isTouch){
17194 this.setActiveBullet(this.indexOfPanel(pan));
17197 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17199 this.transition = true;
17200 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17201 var lr = dir == 'next' ? 'left' : 'right';
17202 pan.el.addClass(dir); // or prev
17203 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17204 cur.el.addClass(lr); // or right
17205 pan.el.addClass(lr);
17208 cur.el.on('transitionend', function() {
17209 Roo.log("trans end?");
17211 pan.el.removeClass([lr,dir]);
17212 pan.setActive(true);
17214 cur.el.removeClass([lr]);
17215 cur.setActive(false);
17217 _this.transition = false;
17219 }, this, { single: true } );
17224 cur.setActive(false);
17225 pan.setActive(true);
17230 showPanelNext : function()
17232 var i = this.indexOfPanel(this.getActivePanel());
17234 if (i >= this.tabs.length - 1 && !this.autoslide) {
17238 if (i >= this.tabs.length - 1 && this.autoslide) {
17242 this.showPanel(this.tabs[i+1]);
17245 showPanelPrev : function()
17247 var i = this.indexOfPanel(this.getActivePanel());
17249 if (i < 1 && !this.autoslide) {
17253 if (i < 1 && this.autoslide) {
17254 i = this.tabs.length;
17257 this.showPanel(this.tabs[i-1]);
17261 addBullet: function()
17263 if(!this.bullets || Roo.isTouch){
17266 var ctr = this.el.select('.carousel-bullets',true).first();
17267 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17268 var bullet = ctr.createChild({
17269 cls : 'bullet bullet-' + i
17270 },ctr.dom.lastChild);
17275 bullet.on('click', (function(e, el, o, ii, t){
17277 e.preventDefault();
17279 this.showPanel(ii);
17281 if(this.autoslide && this.slideFn){
17282 clearInterval(this.slideFn);
17283 this.slideFn = window.setInterval(function() {
17284 _this.showPanelNext();
17288 }).createDelegate(this, [i, bullet], true));
17293 setActiveBullet : function(i)
17299 Roo.each(this.el.select('.bullet', true).elements, function(el){
17300 el.removeClass('selected');
17303 var bullet = this.el.select('.bullet-' + i, true).first();
17309 bullet.addClass('selected');
17320 Roo.apply(Roo.bootstrap.TabGroup, {
17324 * register a Navigation Group
17325 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17327 register : function(navgrp)
17329 this.groups[navgrp.navId] = navgrp;
17333 * fetch a Navigation Group based on the navigation ID
17334 * if one does not exist , it will get created.
17335 * @param {string} the navgroup to add
17336 * @returns {Roo.bootstrap.NavGroup} the navgroup
17338 get: function(navId) {
17339 if (typeof(this.groups[navId]) == 'undefined') {
17340 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17342 return this.groups[navId] ;
17357 * @class Roo.bootstrap.TabPanel
17358 * @extends Roo.bootstrap.Component
17359 * Bootstrap TabPanel class
17360 * @cfg {Boolean} active panel active
17361 * @cfg {String} html panel content
17362 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17363 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17364 * @cfg {String} href click to link..
17368 * Create a new TabPanel
17369 * @param {Object} config The config object
17372 Roo.bootstrap.TabPanel = function(config){
17373 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17377 * Fires when the active status changes
17378 * @param {Roo.bootstrap.TabPanel} this
17379 * @param {Boolean} state the new state
17384 * @event beforedeactivate
17385 * Fires before a tab is de-activated - can be used to do validation on a form.
17386 * @param {Roo.bootstrap.TabPanel} this
17387 * @return {Boolean} false if there is an error
17390 'beforedeactivate': true
17393 this.tabId = this.tabId || Roo.id();
17397 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17405 getAutoCreate : function(){
17408 // item is needed for carousel - not sure if it has any effect otherwise
17409 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17410 html: this.html || ''
17414 cfg.cls += ' active';
17418 cfg.tabId = this.tabId;
17425 initEvents: function()
17427 var p = this.parent();
17429 this.navId = this.navId || p.navId;
17431 if (typeof(this.navId) != 'undefined') {
17432 // not really needed.. but just in case.. parent should be a NavGroup.
17433 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17437 var i = tg.tabs.length - 1;
17439 if(this.active && tg.bullets > 0 && i < tg.bullets){
17440 tg.setActiveBullet(i);
17444 this.el.on('click', this.onClick, this);
17447 this.el.on("touchstart", this.onTouchStart, this);
17448 this.el.on("touchmove", this.onTouchMove, this);
17449 this.el.on("touchend", this.onTouchEnd, this);
17454 onRender : function(ct, position)
17456 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17459 setActive : function(state)
17461 Roo.log("panel - set active " + this.tabId + "=" + state);
17463 this.active = state;
17465 this.el.removeClass('active');
17467 } else if (!this.el.hasClass('active')) {
17468 this.el.addClass('active');
17471 this.fireEvent('changed', this, state);
17474 onClick : function(e)
17476 e.preventDefault();
17478 if(!this.href.length){
17482 window.location.href = this.href;
17491 onTouchStart : function(e)
17493 this.swiping = false;
17495 this.startX = e.browserEvent.touches[0].clientX;
17496 this.startY = e.browserEvent.touches[0].clientY;
17499 onTouchMove : function(e)
17501 this.swiping = true;
17503 this.endX = e.browserEvent.touches[0].clientX;
17504 this.endY = e.browserEvent.touches[0].clientY;
17507 onTouchEnd : function(e)
17514 var tabGroup = this.parent();
17516 if(this.endX > this.startX){ // swiping right
17517 tabGroup.showPanelPrev();
17521 if(this.startX > this.endX){ // swiping left
17522 tabGroup.showPanelNext();
17541 * @class Roo.bootstrap.DateField
17542 * @extends Roo.bootstrap.Input
17543 * Bootstrap DateField class
17544 * @cfg {Number} weekStart default 0
17545 * @cfg {String} viewMode default empty, (months|years)
17546 * @cfg {String} minViewMode default empty, (months|years)
17547 * @cfg {Number} startDate default -Infinity
17548 * @cfg {Number} endDate default Infinity
17549 * @cfg {Boolean} todayHighlight default false
17550 * @cfg {Boolean} todayBtn default false
17551 * @cfg {Boolean} calendarWeeks default false
17552 * @cfg {Object} daysOfWeekDisabled default empty
17553 * @cfg {Boolean} singleMode default false (true | false)
17555 * @cfg {Boolean} keyboardNavigation default true
17556 * @cfg {String} language default en
17559 * Create a new DateField
17560 * @param {Object} config The config object
17563 Roo.bootstrap.DateField = function(config){
17564 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17568 * Fires when this field show.
17569 * @param {Roo.bootstrap.DateField} this
17570 * @param {Mixed} date The date value
17575 * Fires when this field hide.
17576 * @param {Roo.bootstrap.DateField} this
17577 * @param {Mixed} date The date value
17582 * Fires when select a date.
17583 * @param {Roo.bootstrap.DateField} this
17584 * @param {Mixed} date The date value
17588 * @event beforeselect
17589 * Fires when before select a date.
17590 * @param {Roo.bootstrap.DateField} this
17591 * @param {Mixed} date The date value
17593 beforeselect : true
17597 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17600 * @cfg {String} format
17601 * The default date format string which can be overriden for localization support. The format must be
17602 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17606 * @cfg {String} altFormats
17607 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17608 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17610 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17618 todayHighlight : false,
17624 keyboardNavigation: true,
17626 calendarWeeks: false,
17628 startDate: -Infinity,
17632 daysOfWeekDisabled: [],
17636 singleMode : false,
17638 UTCDate: function()
17640 return new Date(Date.UTC.apply(Date, arguments));
17643 UTCToday: function()
17645 var today = new Date();
17646 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17649 getDate: function() {
17650 var d = this.getUTCDate();
17651 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17654 getUTCDate: function() {
17658 setDate: function(d) {
17659 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17662 setUTCDate: function(d) {
17664 this.setValue(this.formatDate(this.date));
17667 onRender: function(ct, position)
17670 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17672 this.language = this.language || 'en';
17673 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17674 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17676 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17677 this.format = this.format || 'm/d/y';
17678 this.isInline = false;
17679 this.isInput = true;
17680 this.component = this.el.select('.add-on', true).first() || false;
17681 this.component = (this.component && this.component.length === 0) ? false : this.component;
17682 this.hasInput = this.component && this.inputEl().length;
17684 if (typeof(this.minViewMode === 'string')) {
17685 switch (this.minViewMode) {
17687 this.minViewMode = 1;
17690 this.minViewMode = 2;
17693 this.minViewMode = 0;
17698 if (typeof(this.viewMode === 'string')) {
17699 switch (this.viewMode) {
17712 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17714 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17716 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17718 this.picker().on('mousedown', this.onMousedown, this);
17719 this.picker().on('click', this.onClick, this);
17721 this.picker().addClass('datepicker-dropdown');
17723 this.startViewMode = this.viewMode;
17725 if(this.singleMode){
17726 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17727 v.setVisibilityMode(Roo.Element.DISPLAY);
17731 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17732 v.setStyle('width', '189px');
17736 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17737 if(!this.calendarWeeks){
17742 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17743 v.attr('colspan', function(i, val){
17744 return parseInt(val) + 1;
17749 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17751 this.setStartDate(this.startDate);
17752 this.setEndDate(this.endDate);
17754 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17761 if(this.isInline) {
17766 picker : function()
17768 return this.pickerEl;
17769 // return this.el.select('.datepicker', true).first();
17772 fillDow: function()
17774 var dowCnt = this.weekStart;
17783 if(this.calendarWeeks){
17791 while (dowCnt < this.weekStart + 7) {
17795 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17799 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17802 fillMonths: function()
17805 var months = this.picker().select('>.datepicker-months td', true).first();
17807 months.dom.innerHTML = '';
17813 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17816 months.createChild(month);
17823 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;
17825 if (this.date < this.startDate) {
17826 this.viewDate = new Date(this.startDate);
17827 } else if (this.date > this.endDate) {
17828 this.viewDate = new Date(this.endDate);
17830 this.viewDate = new Date(this.date);
17838 var d = new Date(this.viewDate),
17839 year = d.getUTCFullYear(),
17840 month = d.getUTCMonth(),
17841 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17842 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17843 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17844 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17845 currentDate = this.date && this.date.valueOf(),
17846 today = this.UTCToday();
17848 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17850 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17852 // this.picker.select('>tfoot th.today').
17853 // .text(dates[this.language].today)
17854 // .toggle(this.todayBtn !== false);
17856 this.updateNavArrows();
17859 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17861 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17863 prevMonth.setUTCDate(day);
17865 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17867 var nextMonth = new Date(prevMonth);
17869 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17871 nextMonth = nextMonth.valueOf();
17873 var fillMonths = false;
17875 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17877 while(prevMonth.valueOf() < nextMonth) {
17880 if (prevMonth.getUTCDay() === this.weekStart) {
17882 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17890 if(this.calendarWeeks){
17891 // ISO 8601: First week contains first thursday.
17892 // ISO also states week starts on Monday, but we can be more abstract here.
17894 // Start of current week: based on weekstart/current date
17895 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17896 // Thursday of this week
17897 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17898 // First Thursday of year, year from thursday
17899 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17900 // Calendar week: ms between thursdays, div ms per day, div 7 days
17901 calWeek = (th - yth) / 864e5 / 7 + 1;
17903 fillMonths.cn.push({
17911 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17913 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17916 if (this.todayHighlight &&
17917 prevMonth.getUTCFullYear() == today.getFullYear() &&
17918 prevMonth.getUTCMonth() == today.getMonth() &&
17919 prevMonth.getUTCDate() == today.getDate()) {
17920 clsName += ' today';
17923 if (currentDate && prevMonth.valueOf() === currentDate) {
17924 clsName += ' active';
17927 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17928 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17929 clsName += ' disabled';
17932 fillMonths.cn.push({
17934 cls: 'day ' + clsName,
17935 html: prevMonth.getDate()
17938 prevMonth.setDate(prevMonth.getDate()+1);
17941 var currentYear = this.date && this.date.getUTCFullYear();
17942 var currentMonth = this.date && this.date.getUTCMonth();
17944 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17946 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17947 v.removeClass('active');
17949 if(currentYear === year && k === currentMonth){
17950 v.addClass('active');
17953 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17954 v.addClass('disabled');
17960 year = parseInt(year/10, 10) * 10;
17962 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17964 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17967 for (var i = -1; i < 11; i++) {
17968 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17970 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17978 showMode: function(dir)
17981 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17984 Roo.each(this.picker().select('>div',true).elements, function(v){
17985 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17988 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17993 if(this.isInline) {
17997 this.picker().removeClass(['bottom', 'top']);
17999 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18001 * place to the top of element!
18005 this.picker().addClass('top');
18006 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18011 this.picker().addClass('bottom');
18013 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18016 parseDate : function(value)
18018 if(!value || value instanceof Date){
18021 var v = Date.parseDate(value, this.format);
18022 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18023 v = Date.parseDate(value, 'Y-m-d');
18025 if(!v && this.altFormats){
18026 if(!this.altFormatsArray){
18027 this.altFormatsArray = this.altFormats.split("|");
18029 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18030 v = Date.parseDate(value, this.altFormatsArray[i]);
18036 formatDate : function(date, fmt)
18038 return (!date || !(date instanceof Date)) ?
18039 date : date.dateFormat(fmt || this.format);
18042 onFocus : function()
18044 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18048 onBlur : function()
18050 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18052 var d = this.inputEl().getValue();
18061 this.picker().show();
18065 this.fireEvent('show', this, this.date);
18070 if(this.isInline) {
18073 this.picker().hide();
18074 this.viewMode = this.startViewMode;
18077 this.fireEvent('hide', this, this.date);
18081 onMousedown: function(e)
18083 e.stopPropagation();
18084 e.preventDefault();
18089 Roo.bootstrap.DateField.superclass.keyup.call(this);
18093 setValue: function(v)
18095 if(this.fireEvent('beforeselect', this, v) !== false){
18096 var d = new Date(this.parseDate(v) ).clearTime();
18098 if(isNaN(d.getTime())){
18099 this.date = this.viewDate = '';
18100 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18104 v = this.formatDate(d);
18106 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18108 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18112 this.fireEvent('select', this, this.date);
18116 getValue: function()
18118 return this.formatDate(this.date);
18121 fireKey: function(e)
18123 if (!this.picker().isVisible()){
18124 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18130 var dateChanged = false,
18132 newDate, newViewDate;
18137 e.preventDefault();
18141 if (!this.keyboardNavigation) {
18144 dir = e.keyCode == 37 ? -1 : 1;
18147 newDate = this.moveYear(this.date, dir);
18148 newViewDate = this.moveYear(this.viewDate, dir);
18149 } else if (e.shiftKey){
18150 newDate = this.moveMonth(this.date, dir);
18151 newViewDate = this.moveMonth(this.viewDate, dir);
18153 newDate = new Date(this.date);
18154 newDate.setUTCDate(this.date.getUTCDate() + dir);
18155 newViewDate = new Date(this.viewDate);
18156 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18158 if (this.dateWithinRange(newDate)){
18159 this.date = newDate;
18160 this.viewDate = newViewDate;
18161 this.setValue(this.formatDate(this.date));
18163 e.preventDefault();
18164 dateChanged = true;
18169 if (!this.keyboardNavigation) {
18172 dir = e.keyCode == 38 ? -1 : 1;
18174 newDate = this.moveYear(this.date, dir);
18175 newViewDate = this.moveYear(this.viewDate, dir);
18176 } else if (e.shiftKey){
18177 newDate = this.moveMonth(this.date, dir);
18178 newViewDate = this.moveMonth(this.viewDate, dir);
18180 newDate = new Date(this.date);
18181 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18182 newViewDate = new Date(this.viewDate);
18183 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18185 if (this.dateWithinRange(newDate)){
18186 this.date = newDate;
18187 this.viewDate = newViewDate;
18188 this.setValue(this.formatDate(this.date));
18190 e.preventDefault();
18191 dateChanged = true;
18195 this.setValue(this.formatDate(this.date));
18197 e.preventDefault();
18200 this.setValue(this.formatDate(this.date));
18214 onClick: function(e)
18216 e.stopPropagation();
18217 e.preventDefault();
18219 var target = e.getTarget();
18221 if(target.nodeName.toLowerCase() === 'i'){
18222 target = Roo.get(target).dom.parentNode;
18225 var nodeName = target.nodeName;
18226 var className = target.className;
18227 var html = target.innerHTML;
18228 //Roo.log(nodeName);
18230 switch(nodeName.toLowerCase()) {
18232 switch(className) {
18238 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18239 switch(this.viewMode){
18241 this.viewDate = this.moveMonth(this.viewDate, dir);
18245 this.viewDate = this.moveYear(this.viewDate, dir);
18251 var date = new Date();
18252 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18254 this.setValue(this.formatDate(this.date));
18261 if (className.indexOf('disabled') < 0) {
18262 this.viewDate.setUTCDate(1);
18263 if (className.indexOf('month') > -1) {
18264 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18266 var year = parseInt(html, 10) || 0;
18267 this.viewDate.setUTCFullYear(year);
18271 if(this.singleMode){
18272 this.setValue(this.formatDate(this.viewDate));
18283 //Roo.log(className);
18284 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18285 var day = parseInt(html, 10) || 1;
18286 var year = this.viewDate.getUTCFullYear(),
18287 month = this.viewDate.getUTCMonth();
18289 if (className.indexOf('old') > -1) {
18296 } else if (className.indexOf('new') > -1) {
18304 //Roo.log([year,month,day]);
18305 this.date = this.UTCDate(year, month, day,0,0,0,0);
18306 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18308 //Roo.log(this.formatDate(this.date));
18309 this.setValue(this.formatDate(this.date));
18316 setStartDate: function(startDate)
18318 this.startDate = startDate || -Infinity;
18319 if (this.startDate !== -Infinity) {
18320 this.startDate = this.parseDate(this.startDate);
18323 this.updateNavArrows();
18326 setEndDate: function(endDate)
18328 this.endDate = endDate || Infinity;
18329 if (this.endDate !== Infinity) {
18330 this.endDate = this.parseDate(this.endDate);
18333 this.updateNavArrows();
18336 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18338 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18339 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18340 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18342 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18343 return parseInt(d, 10);
18346 this.updateNavArrows();
18349 updateNavArrows: function()
18351 if(this.singleMode){
18355 var d = new Date(this.viewDate),
18356 year = d.getUTCFullYear(),
18357 month = d.getUTCMonth();
18359 Roo.each(this.picker().select('.prev', true).elements, function(v){
18361 switch (this.viewMode) {
18364 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18370 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18377 Roo.each(this.picker().select('.next', true).elements, function(v){
18379 switch (this.viewMode) {
18382 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18388 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18396 moveMonth: function(date, dir)
18401 var new_date = new Date(date.valueOf()),
18402 day = new_date.getUTCDate(),
18403 month = new_date.getUTCMonth(),
18404 mag = Math.abs(dir),
18406 dir = dir > 0 ? 1 : -1;
18409 // If going back one month, make sure month is not current month
18410 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18412 return new_date.getUTCMonth() == month;
18414 // If going forward one month, make sure month is as expected
18415 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18417 return new_date.getUTCMonth() != new_month;
18419 new_month = month + dir;
18420 new_date.setUTCMonth(new_month);
18421 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18422 if (new_month < 0 || new_month > 11) {
18423 new_month = (new_month + 12) % 12;
18426 // For magnitudes >1, move one month at a time...
18427 for (var i=0; i<mag; i++) {
18428 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18429 new_date = this.moveMonth(new_date, dir);
18431 // ...then reset the day, keeping it in the new month
18432 new_month = new_date.getUTCMonth();
18433 new_date.setUTCDate(day);
18435 return new_month != new_date.getUTCMonth();
18438 // Common date-resetting loop -- if date is beyond end of month, make it
18441 new_date.setUTCDate(--day);
18442 new_date.setUTCMonth(new_month);
18447 moveYear: function(date, dir)
18449 return this.moveMonth(date, dir*12);
18452 dateWithinRange: function(date)
18454 return date >= this.startDate && date <= this.endDate;
18460 this.picker().remove();
18463 validateValue : function(value)
18465 if(value.length < 1) {
18466 if(this.allowBlank){
18472 if(value.length < this.minLength){
18475 if(value.length > this.maxLength){
18479 var vt = Roo.form.VTypes;
18480 if(!vt[this.vtype](value, this)){
18484 if(typeof this.validator == "function"){
18485 var msg = this.validator(value);
18491 if(this.regex && !this.regex.test(value)){
18495 if(typeof(this.parseDate(value)) == 'undefined'){
18499 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18503 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18513 Roo.apply(Roo.bootstrap.DateField, {
18524 html: '<i class="fa fa-arrow-left"/>'
18534 html: '<i class="fa fa-arrow-right"/>'
18576 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18577 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18578 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18579 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18580 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18593 navFnc: 'FullYear',
18598 navFnc: 'FullYear',
18603 Roo.apply(Roo.bootstrap.DateField, {
18607 cls: 'datepicker dropdown-menu roo-dynamic',
18611 cls: 'datepicker-days',
18615 cls: 'table-condensed',
18617 Roo.bootstrap.DateField.head,
18621 Roo.bootstrap.DateField.footer
18628 cls: 'datepicker-months',
18632 cls: 'table-condensed',
18634 Roo.bootstrap.DateField.head,
18635 Roo.bootstrap.DateField.content,
18636 Roo.bootstrap.DateField.footer
18643 cls: 'datepicker-years',
18647 cls: 'table-condensed',
18649 Roo.bootstrap.DateField.head,
18650 Roo.bootstrap.DateField.content,
18651 Roo.bootstrap.DateField.footer
18670 * @class Roo.bootstrap.TimeField
18671 * @extends Roo.bootstrap.Input
18672 * Bootstrap DateField class
18676 * Create a new TimeField
18677 * @param {Object} config The config object
18680 Roo.bootstrap.TimeField = function(config){
18681 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18685 * Fires when this field show.
18686 * @param {Roo.bootstrap.DateField} thisthis
18687 * @param {Mixed} date The date value
18692 * Fires when this field hide.
18693 * @param {Roo.bootstrap.DateField} this
18694 * @param {Mixed} date The date value
18699 * Fires when select a date.
18700 * @param {Roo.bootstrap.DateField} this
18701 * @param {Mixed} date The date value
18707 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18710 * @cfg {String} format
18711 * The default time format string which can be overriden for localization support. The format must be
18712 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18716 onRender: function(ct, position)
18719 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18721 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18723 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18725 this.pop = this.picker().select('>.datepicker-time',true).first();
18726 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18728 this.picker().on('mousedown', this.onMousedown, this);
18729 this.picker().on('click', this.onClick, this);
18731 this.picker().addClass('datepicker-dropdown');
18736 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18737 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18738 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18739 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18740 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18741 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18745 fireKey: function(e){
18746 if (!this.picker().isVisible()){
18747 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18753 e.preventDefault();
18761 this.onTogglePeriod();
18764 this.onIncrementMinutes();
18767 this.onDecrementMinutes();
18776 onClick: function(e) {
18777 e.stopPropagation();
18778 e.preventDefault();
18781 picker : function()
18783 return this.el.select('.datepicker', true).first();
18786 fillTime: function()
18788 var time = this.pop.select('tbody', true).first();
18790 time.dom.innerHTML = '';
18805 cls: 'hours-up glyphicon glyphicon-chevron-up'
18825 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18846 cls: 'timepicker-hour',
18861 cls: 'timepicker-minute',
18876 cls: 'btn btn-primary period',
18898 cls: 'hours-down glyphicon glyphicon-chevron-down'
18918 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18936 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18943 var hours = this.time.getHours();
18944 var minutes = this.time.getMinutes();
18957 hours = hours - 12;
18961 hours = '0' + hours;
18965 minutes = '0' + minutes;
18968 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18969 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18970 this.pop.select('button', true).first().dom.innerHTML = period;
18976 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18978 var cls = ['bottom'];
18980 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18987 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18992 this.picker().addClass(cls.join('-'));
18996 Roo.each(cls, function(c){
18998 _this.picker().setTop(_this.inputEl().getHeight());
19002 _this.picker().setTop(0 - _this.picker().getHeight());
19007 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19011 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19018 onFocus : function()
19020 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19024 onBlur : function()
19026 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19032 this.picker().show();
19037 this.fireEvent('show', this, this.date);
19042 this.picker().hide();
19045 this.fireEvent('hide', this, this.date);
19048 setTime : function()
19051 this.setValue(this.time.format(this.format));
19053 this.fireEvent('select', this, this.date);
19058 onMousedown: function(e){
19059 e.stopPropagation();
19060 e.preventDefault();
19063 onIncrementHours: function()
19065 Roo.log('onIncrementHours');
19066 this.time = this.time.add(Date.HOUR, 1);
19071 onDecrementHours: function()
19073 Roo.log('onDecrementHours');
19074 this.time = this.time.add(Date.HOUR, -1);
19078 onIncrementMinutes: function()
19080 Roo.log('onIncrementMinutes');
19081 this.time = this.time.add(Date.MINUTE, 1);
19085 onDecrementMinutes: function()
19087 Roo.log('onDecrementMinutes');
19088 this.time = this.time.add(Date.MINUTE, -1);
19092 onTogglePeriod: function()
19094 Roo.log('onTogglePeriod');
19095 this.time = this.time.add(Date.HOUR, 12);
19102 Roo.apply(Roo.bootstrap.TimeField, {
19132 cls: 'btn btn-info ok',
19144 Roo.apply(Roo.bootstrap.TimeField, {
19148 cls: 'datepicker dropdown-menu',
19152 cls: 'datepicker-time',
19156 cls: 'table-condensed',
19158 Roo.bootstrap.TimeField.content,
19159 Roo.bootstrap.TimeField.footer
19178 * @class Roo.bootstrap.MonthField
19179 * @extends Roo.bootstrap.Input
19180 * Bootstrap MonthField class
19182 * @cfg {String} language default en
19185 * Create a new MonthField
19186 * @param {Object} config The config object
19189 Roo.bootstrap.MonthField = function(config){
19190 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19195 * Fires when this field show.
19196 * @param {Roo.bootstrap.MonthField} this
19197 * @param {Mixed} date The date value
19202 * Fires when this field hide.
19203 * @param {Roo.bootstrap.MonthField} this
19204 * @param {Mixed} date The date value
19209 * Fires when select a date.
19210 * @param {Roo.bootstrap.MonthField} this
19211 * @param {String} oldvalue The old value
19212 * @param {String} newvalue The new value
19218 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19220 onRender: function(ct, position)
19223 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19225 this.language = this.language || 'en';
19226 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19227 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19229 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19230 this.isInline = false;
19231 this.isInput = true;
19232 this.component = this.el.select('.add-on', true).first() || false;
19233 this.component = (this.component && this.component.length === 0) ? false : this.component;
19234 this.hasInput = this.component && this.inputEL().length;
19236 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19238 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19240 this.picker().on('mousedown', this.onMousedown, this);
19241 this.picker().on('click', this.onClick, this);
19243 this.picker().addClass('datepicker-dropdown');
19245 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19246 v.setStyle('width', '189px');
19253 if(this.isInline) {
19259 setValue: function(v, suppressEvent)
19261 var o = this.getValue();
19263 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19267 if(suppressEvent !== true){
19268 this.fireEvent('select', this, o, v);
19273 getValue: function()
19278 onClick: function(e)
19280 e.stopPropagation();
19281 e.preventDefault();
19283 var target = e.getTarget();
19285 if(target.nodeName.toLowerCase() === 'i'){
19286 target = Roo.get(target).dom.parentNode;
19289 var nodeName = target.nodeName;
19290 var className = target.className;
19291 var html = target.innerHTML;
19293 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19297 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19299 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19305 picker : function()
19307 return this.pickerEl;
19310 fillMonths: function()
19313 var months = this.picker().select('>.datepicker-months td', true).first();
19315 months.dom.innerHTML = '';
19321 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19324 months.createChild(month);
19333 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19334 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19337 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19338 e.removeClass('active');
19340 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19341 e.addClass('active');
19348 if(this.isInline) {
19352 this.picker().removeClass(['bottom', 'top']);
19354 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19356 * place to the top of element!
19360 this.picker().addClass('top');
19361 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19366 this.picker().addClass('bottom');
19368 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19371 onFocus : function()
19373 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19377 onBlur : function()
19379 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19381 var d = this.inputEl().getValue();
19390 this.picker().show();
19391 this.picker().select('>.datepicker-months', true).first().show();
19395 this.fireEvent('show', this, this.date);
19400 if(this.isInline) {
19403 this.picker().hide();
19404 this.fireEvent('hide', this, this.date);
19408 onMousedown: function(e)
19410 e.stopPropagation();
19411 e.preventDefault();
19416 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19420 fireKey: function(e)
19422 if (!this.picker().isVisible()){
19423 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19434 e.preventDefault();
19438 dir = e.keyCode == 37 ? -1 : 1;
19440 this.vIndex = this.vIndex + dir;
19442 if(this.vIndex < 0){
19446 if(this.vIndex > 11){
19450 if(isNaN(this.vIndex)){
19454 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19460 dir = e.keyCode == 38 ? -1 : 1;
19462 this.vIndex = this.vIndex + dir * 4;
19464 if(this.vIndex < 0){
19468 if(this.vIndex > 11){
19472 if(isNaN(this.vIndex)){
19476 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19481 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19482 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19486 e.preventDefault();
19489 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19490 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19506 this.picker().remove();
19511 Roo.apply(Roo.bootstrap.MonthField, {
19530 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19531 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19536 Roo.apply(Roo.bootstrap.MonthField, {
19540 cls: 'datepicker dropdown-menu roo-dynamic',
19544 cls: 'datepicker-months',
19548 cls: 'table-condensed',
19550 Roo.bootstrap.DateField.content
19570 * @class Roo.bootstrap.CheckBox
19571 * @extends Roo.bootstrap.Input
19572 * Bootstrap CheckBox class
19574 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19575 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19576 * @cfg {String} boxLabel The text that appears beside the checkbox
19577 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19578 * @cfg {Boolean} checked initnal the element
19579 * @cfg {Boolean} inline inline the element (default false)
19580 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19583 * Create a new CheckBox
19584 * @param {Object} config The config object
19587 Roo.bootstrap.CheckBox = function(config){
19588 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19593 * Fires when the element is checked or unchecked.
19594 * @param {Roo.bootstrap.CheckBox} this This input
19595 * @param {Boolean} checked The new checked value
19602 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19604 inputType: 'checkbox',
19612 getAutoCreate : function()
19614 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19620 cfg.cls = 'form-group ' + this.inputType; //input-group
19623 cfg.cls += ' ' + this.inputType + '-inline';
19629 type : this.inputType,
19630 value : this.inputValue,
19631 cls : 'roo-' + this.inputType, //'form-box',
19632 placeholder : this.placeholder || ''
19636 if(this.inputType != 'radio'){
19640 cls : 'roo-hidden-value',
19641 value : this.checked ? this.valueOff : this.inputValue
19646 if (this.weight) { // Validity check?
19647 cfg.cls += " " + this.inputType + "-" + this.weight;
19650 if (this.disabled) {
19651 input.disabled=true;
19655 input.checked = this.checked;
19662 input.name = this.name;
19664 if(this.inputType != 'radio'){
19665 hidden.name = this.name;
19666 input.name = '_hidden_' + this.name;
19671 input.cls += ' input-' + this.size;
19676 ['xs','sm','md','lg'].map(function(size){
19677 if (settings[size]) {
19678 cfg.cls += ' col-' + size + '-' + settings[size];
19682 var inputblock = input;
19684 if (this.before || this.after) {
19687 cls : 'input-group',
19692 inputblock.cn.push({
19694 cls : 'input-group-addon',
19699 inputblock.cn.push(input);
19701 if(this.inputType != 'radio'){
19702 inputblock.cn.push(hidden);
19706 inputblock.cn.push({
19708 cls : 'input-group-addon',
19715 if (align ==='left' && this.fieldLabel.length) {
19716 // Roo.log("left and has label");
19722 cls : 'control-label col-md-' + this.labelWidth,
19723 html : this.fieldLabel
19727 cls : "col-md-" + (12 - this.labelWidth),
19734 } else if ( this.fieldLabel.length) {
19735 // Roo.log(" label");
19739 tag: this.boxLabel ? 'span' : 'label',
19741 cls: 'control-label box-input-label',
19742 //cls : 'input-group-addon',
19743 html : this.fieldLabel
19753 // Roo.log(" no label && no align");
19754 cfg.cn = [ inputblock ] ;
19760 var boxLabelCfg = {
19762 //'for': id, // box label is handled by onclick - so no for...
19764 html: this.boxLabel
19768 boxLabelCfg.tooltip = this.tooltip;
19771 cfg.cn.push(boxLabelCfg);
19774 if(this.inputType != 'radio'){
19775 cfg.cn.push(hidden);
19783 * return the real input element.
19785 inputEl: function ()
19787 return this.el.select('input.roo-' + this.inputType,true).first();
19789 hiddenEl: function ()
19791 return this.el.select('input.roo-hidden-value',true).first();
19794 labelEl: function()
19796 return this.el.select('label.control-label',true).first();
19798 /* depricated... */
19802 return this.labelEl();
19805 boxLabelEl: function()
19807 return this.el.select('label.box-label',true).first();
19810 initEvents : function()
19812 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19814 this.inputEl().on('click', this.onClick, this);
19816 if (this.boxLabel) {
19817 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19820 this.startValue = this.getValue();
19823 Roo.bootstrap.CheckBox.register(this);
19827 onClick : function()
19829 this.setChecked(!this.checked);
19832 setChecked : function(state,suppressEvent)
19834 this.startValue = this.getValue();
19836 if(this.inputType == 'radio'){
19838 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19839 e.dom.checked = false;
19842 this.inputEl().dom.checked = true;
19844 this.inputEl().dom.value = this.inputValue;
19846 if(suppressEvent !== true){
19847 this.fireEvent('check', this, true);
19855 this.checked = state;
19857 this.inputEl().dom.checked = state;
19860 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19862 if(suppressEvent !== true){
19863 this.fireEvent('check', this, state);
19869 getValue : function()
19871 if(this.inputType == 'radio'){
19872 return this.getGroupValue();
19875 return this.hiddenEl().dom.value;
19879 getGroupValue : function()
19881 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19885 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19888 setValue : function(v,suppressEvent)
19890 if(this.inputType == 'radio'){
19891 this.setGroupValue(v, suppressEvent);
19895 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19900 setGroupValue : function(v, suppressEvent)
19902 this.startValue = this.getValue();
19904 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19905 e.dom.checked = false;
19907 if(e.dom.value == v){
19908 e.dom.checked = true;
19912 if(suppressEvent !== true){
19913 this.fireEvent('check', this, true);
19921 validate : function()
19925 (this.inputType == 'radio' && this.validateRadio()) ||
19926 (this.inputType == 'checkbox' && this.validateCheckbox())
19932 this.markInvalid();
19936 validateRadio : function()
19938 if(this.allowBlank){
19944 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19945 if(!e.dom.checked){
19957 validateCheckbox : function()
19960 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19963 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19971 for(var i in group){
19976 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19983 * Mark this field as valid
19985 markValid : function()
19989 this.fireEvent('valid', this);
19991 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19994 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20001 if(this.inputType == 'radio'){
20002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20003 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20004 e.findParent('.form-group', false, true).addClass(_this.validClass);
20011 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20012 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20016 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20022 for(var i in group){
20023 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20024 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20029 * Mark this field as invalid
20030 * @param {String} msg The validation message
20032 markInvalid : function(msg)
20034 if(this.allowBlank){
20040 this.fireEvent('invalid', this, msg);
20042 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20045 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20049 label.markInvalid();
20052 if(this.inputType == 'radio'){
20053 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20054 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20055 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20062 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20063 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20067 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20073 for(var i in group){
20074 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20075 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20080 clearInvalid : function()
20082 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20084 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20087 label.iconEl.removeClass(label.validClass);
20088 label.iconEl.removeClass(label.invalidClass);
20092 disable : function()
20094 if(this.inputType != 'radio'){
20095 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20102 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20103 _this.getActionEl().addClass(this.disabledClass);
20104 e.dom.disabled = true;
20108 this.disabled = true;
20109 this.fireEvent("disable", this);
20113 enable : function()
20115 if(this.inputType != 'radio'){
20116 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20123 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20124 _this.getActionEl().removeClass(this.disabledClass);
20125 e.dom.disabled = false;
20129 this.disabled = false;
20130 this.fireEvent("enable", this);
20136 Roo.apply(Roo.bootstrap.CheckBox, {
20141 * register a CheckBox Group
20142 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20144 register : function(checkbox)
20146 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20147 this.groups[checkbox.groupId] = {};
20150 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20154 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20158 * fetch a CheckBox Group based on the group ID
20159 * @param {string} the group ID
20160 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20162 get: function(groupId) {
20163 if (typeof(this.groups[groupId]) == 'undefined') {
20167 return this.groups[groupId] ;
20180 * @class Roo.bootstrap.Radio
20181 * @extends Roo.bootstrap.Component
20182 * Bootstrap Radio class
20183 * @cfg {String} boxLabel - the label associated
20184 * @cfg {String} value - the value of radio
20187 * Create a new Radio
20188 * @param {Object} config The config object
20190 Roo.bootstrap.Radio = function(config){
20191 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20195 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20201 getAutoCreate : function()
20205 cls : 'form-group radio',
20210 html : this.boxLabel
20218 initEvents : function()
20220 this.parent().register(this);
20222 this.el.on('click', this.onClick, this);
20226 onClick : function()
20228 this.setChecked(true);
20231 setChecked : function(state, suppressEvent)
20233 this.parent().setValue(this.value, suppressEvent);
20240 //<script type="text/javascript">
20243 * Based Ext JS Library 1.1.1
20244 * Copyright(c) 2006-2007, Ext JS, LLC.
20250 * @class Roo.HtmlEditorCore
20251 * @extends Roo.Component
20252 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20254 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20257 Roo.HtmlEditorCore = function(config){
20260 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20265 * @event initialize
20266 * Fires when the editor is fully initialized (including the iframe)
20267 * @param {Roo.HtmlEditorCore} this
20272 * Fires when the editor is first receives the focus. Any insertion must wait
20273 * until after this event.
20274 * @param {Roo.HtmlEditorCore} this
20278 * @event beforesync
20279 * Fires before the textarea is updated with content from the editor iframe. Return false
20280 * to cancel the sync.
20281 * @param {Roo.HtmlEditorCore} this
20282 * @param {String} html
20286 * @event beforepush
20287 * Fires before the iframe editor is updated with content from the textarea. Return false
20288 * to cancel the push.
20289 * @param {Roo.HtmlEditorCore} this
20290 * @param {String} html
20295 * Fires when the textarea is updated with content from the editor iframe.
20296 * @param {Roo.HtmlEditorCore} this
20297 * @param {String} html
20302 * Fires when the iframe editor is updated with content from the textarea.
20303 * @param {Roo.HtmlEditorCore} this
20304 * @param {String} html
20309 * @event editorevent
20310 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20311 * @param {Roo.HtmlEditorCore} this
20317 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20319 // defaults : white / black...
20320 this.applyBlacklists();
20327 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20331 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20337 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20342 * @cfg {Number} height (in pixels)
20346 * @cfg {Number} width (in pixels)
20351 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20354 stylesheets: false,
20359 // private properties
20360 validationEvent : false,
20362 initialized : false,
20364 sourceEditMode : false,
20365 onFocus : Roo.emptyFn,
20367 hideMode:'offsets',
20371 // blacklist + whitelisted elements..
20378 * Protected method that will not generally be called directly. It
20379 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20380 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20382 getDocMarkup : function(){
20386 // inherit styels from page...??
20387 if (this.stylesheets === false) {
20389 Roo.get(document.head).select('style').each(function(node) {
20390 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20393 Roo.get(document.head).select('link').each(function(node) {
20394 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20397 } else if (!this.stylesheets.length) {
20399 st = '<style type="text/css">' +
20400 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20406 st += '<style type="text/css">' +
20407 'IMG { cursor: pointer } ' +
20411 return '<html><head>' + st +
20412 //<style type="text/css">' +
20413 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20415 ' </head><body class="roo-htmleditor-body"></body></html>';
20419 onRender : function(ct, position)
20422 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20423 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20426 this.el.dom.style.border = '0 none';
20427 this.el.dom.setAttribute('tabIndex', -1);
20428 this.el.addClass('x-hidden hide');
20432 if(Roo.isIE){ // fix IE 1px bogus margin
20433 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20437 this.frameId = Roo.id();
20441 var iframe = this.owner.wrap.createChild({
20443 cls: 'form-control', // bootstrap..
20445 name: this.frameId,
20446 frameBorder : 'no',
20447 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20452 this.iframe = iframe.dom;
20454 this.assignDocWin();
20456 this.doc.designMode = 'on';
20459 this.doc.write(this.getDocMarkup());
20463 var task = { // must defer to wait for browser to be ready
20465 //console.log("run task?" + this.doc.readyState);
20466 this.assignDocWin();
20467 if(this.doc.body || this.doc.readyState == 'complete'){
20469 this.doc.designMode="on";
20473 Roo.TaskMgr.stop(task);
20474 this.initEditor.defer(10, this);
20481 Roo.TaskMgr.start(task);
20486 onResize : function(w, h)
20488 Roo.log('resize: ' +w + ',' + h );
20489 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20493 if(typeof w == 'number'){
20495 this.iframe.style.width = w + 'px';
20497 if(typeof h == 'number'){
20499 this.iframe.style.height = h + 'px';
20501 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20508 * Toggles the editor between standard and source edit mode.
20509 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20511 toggleSourceEdit : function(sourceEditMode){
20513 this.sourceEditMode = sourceEditMode === true;
20515 if(this.sourceEditMode){
20517 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20520 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20521 //this.iframe.className = '';
20524 //this.setSize(this.owner.wrap.getSize());
20525 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20532 * Protected method that will not generally be called directly. If you need/want
20533 * custom HTML cleanup, this is the method you should override.
20534 * @param {String} html The HTML to be cleaned
20535 * return {String} The cleaned HTML
20537 cleanHtml : function(html){
20538 html = String(html);
20539 if(html.length > 5){
20540 if(Roo.isSafari){ // strip safari nonsense
20541 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20544 if(html == ' '){
20551 * HTML Editor -> Textarea
20552 * Protected method that will not generally be called directly. Syncs the contents
20553 * of the editor iframe with the textarea.
20555 syncValue : function(){
20556 if(this.initialized){
20557 var bd = (this.doc.body || this.doc.documentElement);
20558 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20559 var html = bd.innerHTML;
20561 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20562 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20564 html = '<div style="'+m[0]+'">' + html + '</div>';
20567 html = this.cleanHtml(html);
20568 // fix up the special chars.. normaly like back quotes in word...
20569 // however we do not want to do this with chinese..
20570 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20571 var cc = b.charCodeAt();
20573 (cc >= 0x4E00 && cc < 0xA000 ) ||
20574 (cc >= 0x3400 && cc < 0x4E00 ) ||
20575 (cc >= 0xf900 && cc < 0xfb00 )
20581 if(this.owner.fireEvent('beforesync', this, html) !== false){
20582 this.el.dom.value = html;
20583 this.owner.fireEvent('sync', this, html);
20589 * Protected method that will not generally be called directly. Pushes the value of the textarea
20590 * into the iframe editor.
20592 pushValue : function(){
20593 if(this.initialized){
20594 var v = this.el.dom.value.trim();
20596 // if(v.length < 1){
20600 if(this.owner.fireEvent('beforepush', this, v) !== false){
20601 var d = (this.doc.body || this.doc.documentElement);
20603 this.cleanUpPaste();
20604 this.el.dom.value = d.innerHTML;
20605 this.owner.fireEvent('push', this, v);
20611 deferFocus : function(){
20612 this.focus.defer(10, this);
20616 focus : function(){
20617 if(this.win && !this.sourceEditMode){
20624 assignDocWin: function()
20626 var iframe = this.iframe;
20629 this.doc = iframe.contentWindow.document;
20630 this.win = iframe.contentWindow;
20632 // if (!Roo.get(this.frameId)) {
20635 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20636 // this.win = Roo.get(this.frameId).dom.contentWindow;
20638 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20642 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20643 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20648 initEditor : function(){
20649 //console.log("INIT EDITOR");
20650 this.assignDocWin();
20654 this.doc.designMode="on";
20656 this.doc.write(this.getDocMarkup());
20659 var dbody = (this.doc.body || this.doc.documentElement);
20660 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20661 // this copies styles from the containing element into thsi one..
20662 // not sure why we need all of this..
20663 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20665 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20666 //ss['background-attachment'] = 'fixed'; // w3c
20667 dbody.bgProperties = 'fixed'; // ie
20668 //Roo.DomHelper.applyStyles(dbody, ss);
20669 Roo.EventManager.on(this.doc, {
20670 //'mousedown': this.onEditorEvent,
20671 'mouseup': this.onEditorEvent,
20672 'dblclick': this.onEditorEvent,
20673 'click': this.onEditorEvent,
20674 'keyup': this.onEditorEvent,
20679 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20681 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20682 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20684 this.initialized = true;
20686 this.owner.fireEvent('initialize', this);
20691 onDestroy : function(){
20697 //for (var i =0; i < this.toolbars.length;i++) {
20698 // // fixme - ask toolbars for heights?
20699 // this.toolbars[i].onDestroy();
20702 //this.wrap.dom.innerHTML = '';
20703 //this.wrap.remove();
20708 onFirstFocus : function(){
20710 this.assignDocWin();
20713 this.activated = true;
20716 if(Roo.isGecko){ // prevent silly gecko errors
20718 var s = this.win.getSelection();
20719 if(!s.focusNode || s.focusNode.nodeType != 3){
20720 var r = s.getRangeAt(0);
20721 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20726 this.execCmd('useCSS', true);
20727 this.execCmd('styleWithCSS', false);
20730 this.owner.fireEvent('activate', this);
20734 adjustFont: function(btn){
20735 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20736 //if(Roo.isSafari){ // safari
20739 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20740 if(Roo.isSafari){ // safari
20741 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20742 v = (v < 10) ? 10 : v;
20743 v = (v > 48) ? 48 : v;
20744 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20749 v = Math.max(1, v+adjust);
20751 this.execCmd('FontSize', v );
20754 onEditorEvent : function(e)
20756 this.owner.fireEvent('editorevent', this, e);
20757 // this.updateToolbar();
20758 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20761 insertTag : function(tg)
20763 // could be a bit smarter... -> wrap the current selected tRoo..
20764 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20766 range = this.createRange(this.getSelection());
20767 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20768 wrappingNode.appendChild(range.extractContents());
20769 range.insertNode(wrappingNode);
20776 this.execCmd("formatblock", tg);
20780 insertText : function(txt)
20784 var range = this.createRange();
20785 range.deleteContents();
20786 //alert(Sender.getAttribute('label'));
20788 range.insertNode(this.doc.createTextNode(txt));
20794 * Executes a Midas editor command on the editor document and performs necessary focus and
20795 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20796 * @param {String} cmd The Midas command
20797 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20799 relayCmd : function(cmd, value){
20801 this.execCmd(cmd, value);
20802 this.owner.fireEvent('editorevent', this);
20803 //this.updateToolbar();
20804 this.owner.deferFocus();
20808 * Executes a Midas editor command directly on the editor document.
20809 * For visual commands, you should use {@link #relayCmd} instead.
20810 * <b>This should only be called after the editor is initialized.</b>
20811 * @param {String} cmd The Midas command
20812 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20814 execCmd : function(cmd, value){
20815 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20822 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20824 * @param {String} text | dom node..
20826 insertAtCursor : function(text)
20831 if(!this.activated){
20837 var r = this.doc.selection.createRange();
20848 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20852 // from jquery ui (MIT licenced)
20854 var win = this.win;
20856 if (win.getSelection && win.getSelection().getRangeAt) {
20857 range = win.getSelection().getRangeAt(0);
20858 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20859 range.insertNode(node);
20860 } else if (win.document.selection && win.document.selection.createRange) {
20861 // no firefox support
20862 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20863 win.document.selection.createRange().pasteHTML(txt);
20865 // no firefox support
20866 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20867 this.execCmd('InsertHTML', txt);
20876 mozKeyPress : function(e){
20878 var c = e.getCharCode(), cmd;
20881 c = String.fromCharCode(c).toLowerCase();
20895 this.cleanUpPaste.defer(100, this);
20903 e.preventDefault();
20911 fixKeys : function(){ // load time branching for fastest keydown performance
20913 return function(e){
20914 var k = e.getKey(), r;
20917 r = this.doc.selection.createRange();
20920 r.pasteHTML('    ');
20927 r = this.doc.selection.createRange();
20929 var target = r.parentElement();
20930 if(!target || target.tagName.toLowerCase() != 'li'){
20932 r.pasteHTML('<br />');
20938 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20939 this.cleanUpPaste.defer(100, this);
20945 }else if(Roo.isOpera){
20946 return function(e){
20947 var k = e.getKey();
20951 this.execCmd('InsertHTML','    ');
20954 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20955 this.cleanUpPaste.defer(100, this);
20960 }else if(Roo.isSafari){
20961 return function(e){
20962 var k = e.getKey();
20966 this.execCmd('InsertText','\t');
20970 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20971 this.cleanUpPaste.defer(100, this);
20979 getAllAncestors: function()
20981 var p = this.getSelectedNode();
20984 a.push(p); // push blank onto stack..
20985 p = this.getParentElement();
20989 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20993 a.push(this.doc.body);
20997 lastSelNode : false,
21000 getSelection : function()
21002 this.assignDocWin();
21003 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21006 getSelectedNode: function()
21008 // this may only work on Gecko!!!
21010 // should we cache this!!!!
21015 var range = this.createRange(this.getSelection()).cloneRange();
21018 var parent = range.parentElement();
21020 var testRange = range.duplicate();
21021 testRange.moveToElementText(parent);
21022 if (testRange.inRange(range)) {
21025 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21028 parent = parent.parentElement;
21033 // is ancestor a text element.
21034 var ac = range.commonAncestorContainer;
21035 if (ac.nodeType == 3) {
21036 ac = ac.parentNode;
21039 var ar = ac.childNodes;
21042 var other_nodes = [];
21043 var has_other_nodes = false;
21044 for (var i=0;i<ar.length;i++) {
21045 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21048 // fullly contained node.
21050 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21055 // probably selected..
21056 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21057 other_nodes.push(ar[i]);
21061 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21066 has_other_nodes = true;
21068 if (!nodes.length && other_nodes.length) {
21069 nodes= other_nodes;
21071 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21077 createRange: function(sel)
21079 // this has strange effects when using with
21080 // top toolbar - not sure if it's a great idea.
21081 //this.editor.contentWindow.focus();
21082 if (typeof sel != "undefined") {
21084 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21086 return this.doc.createRange();
21089 return this.doc.createRange();
21092 getParentElement: function()
21095 this.assignDocWin();
21096 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21098 var range = this.createRange(sel);
21101 var p = range.commonAncestorContainer;
21102 while (p.nodeType == 3) { // text node
21113 * Range intersection.. the hard stuff...
21117 * [ -- selected range --- ]
21121 * if end is before start or hits it. fail.
21122 * if start is after end or hits it fail.
21124 * if either hits (but other is outside. - then it's not
21130 // @see http://www.thismuchiknow.co.uk/?p=64.
21131 rangeIntersectsNode : function(range, node)
21133 var nodeRange = node.ownerDocument.createRange();
21135 nodeRange.selectNode(node);
21137 nodeRange.selectNodeContents(node);
21140 var rangeStartRange = range.cloneRange();
21141 rangeStartRange.collapse(true);
21143 var rangeEndRange = range.cloneRange();
21144 rangeEndRange.collapse(false);
21146 var nodeStartRange = nodeRange.cloneRange();
21147 nodeStartRange.collapse(true);
21149 var nodeEndRange = nodeRange.cloneRange();
21150 nodeEndRange.collapse(false);
21152 return rangeStartRange.compareBoundaryPoints(
21153 Range.START_TO_START, nodeEndRange) == -1 &&
21154 rangeEndRange.compareBoundaryPoints(
21155 Range.START_TO_START, nodeStartRange) == 1;
21159 rangeCompareNode : function(range, node)
21161 var nodeRange = node.ownerDocument.createRange();
21163 nodeRange.selectNode(node);
21165 nodeRange.selectNodeContents(node);
21169 range.collapse(true);
21171 nodeRange.collapse(true);
21173 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21174 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21176 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21178 var nodeIsBefore = ss == 1;
21179 var nodeIsAfter = ee == -1;
21181 if (nodeIsBefore && nodeIsAfter) {
21184 if (!nodeIsBefore && nodeIsAfter) {
21185 return 1; //right trailed.
21188 if (nodeIsBefore && !nodeIsAfter) {
21189 return 2; // left trailed.
21195 // private? - in a new class?
21196 cleanUpPaste : function()
21198 // cleans up the whole document..
21199 Roo.log('cleanuppaste');
21201 this.cleanUpChildren(this.doc.body);
21202 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21203 if (clean != this.doc.body.innerHTML) {
21204 this.doc.body.innerHTML = clean;
21209 cleanWordChars : function(input) {// change the chars to hex code
21210 var he = Roo.HtmlEditorCore;
21212 var output = input;
21213 Roo.each(he.swapCodes, function(sw) {
21214 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21216 output = output.replace(swapper, sw[1]);
21223 cleanUpChildren : function (n)
21225 if (!n.childNodes.length) {
21228 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21229 this.cleanUpChild(n.childNodes[i]);
21236 cleanUpChild : function (node)
21239 //console.log(node);
21240 if (node.nodeName == "#text") {
21241 // clean up silly Windows -- stuff?
21244 if (node.nodeName == "#comment") {
21245 node.parentNode.removeChild(node);
21246 // clean up silly Windows -- stuff?
21249 var lcname = node.tagName.toLowerCase();
21250 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21251 // whitelist of tags..
21253 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21255 node.parentNode.removeChild(node);
21260 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21262 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21263 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21265 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21266 // remove_keep_children = true;
21269 if (remove_keep_children) {
21270 this.cleanUpChildren(node);
21271 // inserts everything just before this node...
21272 while (node.childNodes.length) {
21273 var cn = node.childNodes[0];
21274 node.removeChild(cn);
21275 node.parentNode.insertBefore(cn, node);
21277 node.parentNode.removeChild(node);
21281 if (!node.attributes || !node.attributes.length) {
21282 this.cleanUpChildren(node);
21286 function cleanAttr(n,v)
21289 if (v.match(/^\./) || v.match(/^\//)) {
21292 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21295 if (v.match(/^#/)) {
21298 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21299 node.removeAttribute(n);
21303 var cwhite = this.cwhite;
21304 var cblack = this.cblack;
21306 function cleanStyle(n,v)
21308 if (v.match(/expression/)) { //XSS?? should we even bother..
21309 node.removeAttribute(n);
21313 var parts = v.split(/;/);
21316 Roo.each(parts, function(p) {
21317 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21321 var l = p.split(':').shift().replace(/\s+/g,'');
21322 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21324 if ( cwhite.length && cblack.indexOf(l) > -1) {
21325 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21326 //node.removeAttribute(n);
21330 // only allow 'c whitelisted system attributes'
21331 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21332 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21333 //node.removeAttribute(n);
21343 if (clean.length) {
21344 node.setAttribute(n, clean.join(';'));
21346 node.removeAttribute(n);
21352 for (var i = node.attributes.length-1; i > -1 ; i--) {
21353 var a = node.attributes[i];
21356 if (a.name.toLowerCase().substr(0,2)=='on') {
21357 node.removeAttribute(a.name);
21360 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21361 node.removeAttribute(a.name);
21364 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21365 cleanAttr(a.name,a.value); // fixme..
21368 if (a.name == 'style') {
21369 cleanStyle(a.name,a.value);
21372 /// clean up MS crap..
21373 // tecnically this should be a list of valid class'es..
21376 if (a.name == 'class') {
21377 if (a.value.match(/^Mso/)) {
21378 node.className = '';
21381 if (a.value.match(/body/)) {
21382 node.className = '';
21393 this.cleanUpChildren(node);
21399 * Clean up MS wordisms...
21401 cleanWord : function(node)
21406 this.cleanWord(this.doc.body);
21409 if (node.nodeName == "#text") {
21410 // clean up silly Windows -- stuff?
21413 if (node.nodeName == "#comment") {
21414 node.parentNode.removeChild(node);
21415 // clean up silly Windows -- stuff?
21419 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21420 node.parentNode.removeChild(node);
21424 // remove - but keep children..
21425 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21426 while (node.childNodes.length) {
21427 var cn = node.childNodes[0];
21428 node.removeChild(cn);
21429 node.parentNode.insertBefore(cn, node);
21431 node.parentNode.removeChild(node);
21432 this.iterateChildren(node, this.cleanWord);
21436 if (node.className.length) {
21438 var cn = node.className.split(/\W+/);
21440 Roo.each(cn, function(cls) {
21441 if (cls.match(/Mso[a-zA-Z]+/)) {
21446 node.className = cna.length ? cna.join(' ') : '';
21448 node.removeAttribute("class");
21452 if (node.hasAttribute("lang")) {
21453 node.removeAttribute("lang");
21456 if (node.hasAttribute("style")) {
21458 var styles = node.getAttribute("style").split(";");
21460 Roo.each(styles, function(s) {
21461 if (!s.match(/:/)) {
21464 var kv = s.split(":");
21465 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21468 // what ever is left... we allow.
21471 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21472 if (!nstyle.length) {
21473 node.removeAttribute('style');
21476 this.iterateChildren(node, this.cleanWord);
21482 * iterateChildren of a Node, calling fn each time, using this as the scole..
21483 * @param {DomNode} node node to iterate children of.
21484 * @param {Function} fn method of this class to call on each item.
21486 iterateChildren : function(node, fn)
21488 if (!node.childNodes.length) {
21491 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21492 fn.call(this, node.childNodes[i])
21498 * cleanTableWidths.
21500 * Quite often pasting from word etc.. results in tables with column and widths.
21501 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21504 cleanTableWidths : function(node)
21509 this.cleanTableWidths(this.doc.body);
21514 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21517 Roo.log(node.tagName);
21518 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21519 this.iterateChildren(node, this.cleanTableWidths);
21522 if (node.hasAttribute('width')) {
21523 node.removeAttribute('width');
21527 if (node.hasAttribute("style")) {
21530 var styles = node.getAttribute("style").split(";");
21532 Roo.each(styles, function(s) {
21533 if (!s.match(/:/)) {
21536 var kv = s.split(":");
21537 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21540 // what ever is left... we allow.
21543 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21544 if (!nstyle.length) {
21545 node.removeAttribute('style');
21549 this.iterateChildren(node, this.cleanTableWidths);
21557 domToHTML : function(currentElement, depth, nopadtext) {
21559 depth = depth || 0;
21560 nopadtext = nopadtext || false;
21562 if (!currentElement) {
21563 return this.domToHTML(this.doc.body);
21566 //Roo.log(currentElement);
21568 var allText = false;
21569 var nodeName = currentElement.nodeName;
21570 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21572 if (nodeName == '#text') {
21574 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21579 if (nodeName != 'BODY') {
21582 // Prints the node tagName, such as <A>, <IMG>, etc
21585 for(i = 0; i < currentElement.attributes.length;i++) {
21587 var aname = currentElement.attributes.item(i).name;
21588 if (!currentElement.attributes.item(i).value.length) {
21591 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21594 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21603 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21606 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21611 // Traverse the tree
21613 var currentElementChild = currentElement.childNodes.item(i);
21614 var allText = true;
21615 var innerHTML = '';
21617 while (currentElementChild) {
21618 // Formatting code (indent the tree so it looks nice on the screen)
21619 var nopad = nopadtext;
21620 if (lastnode == 'SPAN') {
21624 if (currentElementChild.nodeName == '#text') {
21625 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21626 toadd = nopadtext ? toadd : toadd.trim();
21627 if (!nopad && toadd.length > 80) {
21628 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21630 innerHTML += toadd;
21633 currentElementChild = currentElement.childNodes.item(i);
21639 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21641 // Recursively traverse the tree structure of the child node
21642 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21643 lastnode = currentElementChild.nodeName;
21645 currentElementChild=currentElement.childNodes.item(i);
21651 // The remaining code is mostly for formatting the tree
21652 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21657 ret+= "</"+tagName+">";
21663 applyBlacklists : function()
21665 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21666 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21670 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21671 if (b.indexOf(tag) > -1) {
21674 this.white.push(tag);
21678 Roo.each(w, function(tag) {
21679 if (b.indexOf(tag) > -1) {
21682 if (this.white.indexOf(tag) > -1) {
21685 this.white.push(tag);
21690 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21691 if (w.indexOf(tag) > -1) {
21694 this.black.push(tag);
21698 Roo.each(b, function(tag) {
21699 if (w.indexOf(tag) > -1) {
21702 if (this.black.indexOf(tag) > -1) {
21705 this.black.push(tag);
21710 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21711 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21715 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21716 if (b.indexOf(tag) > -1) {
21719 this.cwhite.push(tag);
21723 Roo.each(w, function(tag) {
21724 if (b.indexOf(tag) > -1) {
21727 if (this.cwhite.indexOf(tag) > -1) {
21730 this.cwhite.push(tag);
21735 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21736 if (w.indexOf(tag) > -1) {
21739 this.cblack.push(tag);
21743 Roo.each(b, function(tag) {
21744 if (w.indexOf(tag) > -1) {
21747 if (this.cblack.indexOf(tag) > -1) {
21750 this.cblack.push(tag);
21755 setStylesheets : function(stylesheets)
21757 if(typeof(stylesheets) == 'string'){
21758 Roo.get(this.iframe.contentDocument.head).createChild({
21760 rel : 'stylesheet',
21769 Roo.each(stylesheets, function(s) {
21774 Roo.get(_this.iframe.contentDocument.head).createChild({
21776 rel : 'stylesheet',
21785 removeStylesheets : function()
21789 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21794 // hide stuff that is not compatible
21808 * @event specialkey
21812 * @cfg {String} fieldClass @hide
21815 * @cfg {String} focusClass @hide
21818 * @cfg {String} autoCreate @hide
21821 * @cfg {String} inputType @hide
21824 * @cfg {String} invalidClass @hide
21827 * @cfg {String} invalidText @hide
21830 * @cfg {String} msgFx @hide
21833 * @cfg {String} validateOnBlur @hide
21837 Roo.HtmlEditorCore.white = [
21838 'area', 'br', 'img', 'input', 'hr', 'wbr',
21840 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21841 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21842 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21843 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21844 'table', 'ul', 'xmp',
21846 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21849 'dir', 'menu', 'ol', 'ul', 'dl',
21855 Roo.HtmlEditorCore.black = [
21856 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21858 'base', 'basefont', 'bgsound', 'blink', 'body',
21859 'frame', 'frameset', 'head', 'html', 'ilayer',
21860 'iframe', 'layer', 'link', 'meta', 'object',
21861 'script', 'style' ,'title', 'xml' // clean later..
21863 Roo.HtmlEditorCore.clean = [
21864 'script', 'style', 'title', 'xml'
21866 Roo.HtmlEditorCore.remove = [
21871 Roo.HtmlEditorCore.ablack = [
21875 Roo.HtmlEditorCore.aclean = [
21876 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21880 Roo.HtmlEditorCore.pwhite= [
21881 'http', 'https', 'mailto'
21884 // white listed style attributes.
21885 Roo.HtmlEditorCore.cwhite= [
21886 // 'text-align', /// default is to allow most things..
21892 // black listed style attributes.
21893 Roo.HtmlEditorCore.cblack= [
21894 // 'font-size' -- this can be set by the project
21898 Roo.HtmlEditorCore.swapCodes =[
21917 * @class Roo.bootstrap.HtmlEditor
21918 * @extends Roo.bootstrap.TextArea
21919 * Bootstrap HtmlEditor class
21922 * Create a new HtmlEditor
21923 * @param {Object} config The config object
21926 Roo.bootstrap.HtmlEditor = function(config){
21927 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21928 if (!this.toolbars) {
21929 this.toolbars = [];
21931 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21934 * @event initialize
21935 * Fires when the editor is fully initialized (including the iframe)
21936 * @param {HtmlEditor} this
21941 * Fires when the editor is first receives the focus. Any insertion must wait
21942 * until after this event.
21943 * @param {HtmlEditor} this
21947 * @event beforesync
21948 * Fires before the textarea is updated with content from the editor iframe. Return false
21949 * to cancel the sync.
21950 * @param {HtmlEditor} this
21951 * @param {String} html
21955 * @event beforepush
21956 * Fires before the iframe editor is updated with content from the textarea. Return false
21957 * to cancel the push.
21958 * @param {HtmlEditor} this
21959 * @param {String} html
21964 * Fires when the textarea is updated with content from the editor iframe.
21965 * @param {HtmlEditor} this
21966 * @param {String} html
21971 * Fires when the iframe editor is updated with content from the textarea.
21972 * @param {HtmlEditor} this
21973 * @param {String} html
21977 * @event editmodechange
21978 * Fires when the editor switches edit modes
21979 * @param {HtmlEditor} this
21980 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21982 editmodechange: true,
21984 * @event editorevent
21985 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21986 * @param {HtmlEditor} this
21990 * @event firstfocus
21991 * Fires when on first focus - needed by toolbars..
21992 * @param {HtmlEditor} this
21997 * Auto save the htmlEditor value as a file into Events
21998 * @param {HtmlEditor} this
22002 * @event savedpreview
22003 * preview the saved version of htmlEditor
22004 * @param {HtmlEditor} this
22011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22015 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22020 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22025 * @cfg {Number} height (in pixels)
22029 * @cfg {Number} width (in pixels)
22034 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22037 stylesheets: false,
22042 // private properties
22043 validationEvent : false,
22045 initialized : false,
22048 onFocus : Roo.emptyFn,
22050 hideMode:'offsets',
22053 tbContainer : false,
22055 toolbarContainer :function() {
22056 return this.wrap.select('.x-html-editor-tb',true).first();
22060 * Protected method that will not generally be called directly. It
22061 * is called when the editor creates its toolbar. Override this method if you need to
22062 * add custom toolbar buttons.
22063 * @param {HtmlEditor} editor
22065 createToolbar : function(){
22067 Roo.log("create toolbars");
22069 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22070 this.toolbars[0].render(this.toolbarContainer());
22074 // if (!editor.toolbars || !editor.toolbars.length) {
22075 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22078 // for (var i =0 ; i < editor.toolbars.length;i++) {
22079 // editor.toolbars[i] = Roo.factory(
22080 // typeof(editor.toolbars[i]) == 'string' ?
22081 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22082 // Roo.bootstrap.HtmlEditor);
22083 // editor.toolbars[i].init(editor);
22089 onRender : function(ct, position)
22091 // Roo.log("Call onRender: " + this.xtype);
22093 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22095 this.wrap = this.inputEl().wrap({
22096 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22099 this.editorcore.onRender(ct, position);
22101 if (this.resizable) {
22102 this.resizeEl = new Roo.Resizable(this.wrap, {
22106 minHeight : this.height,
22107 height: this.height,
22108 handles : this.resizable,
22111 resize : function(r, w, h) {
22112 _t.onResize(w,h); // -something
22118 this.createToolbar(this);
22121 if(!this.width && this.resizable){
22122 this.setSize(this.wrap.getSize());
22124 if (this.resizeEl) {
22125 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22126 // should trigger onReize..
22132 onResize : function(w, h)
22134 Roo.log('resize: ' +w + ',' + h );
22135 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22139 if(this.inputEl() ){
22140 if(typeof w == 'number'){
22141 var aw = w - this.wrap.getFrameWidth('lr');
22142 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22145 if(typeof h == 'number'){
22146 var tbh = -11; // fixme it needs to tool bar size!
22147 for (var i =0; i < this.toolbars.length;i++) {
22148 // fixme - ask toolbars for heights?
22149 tbh += this.toolbars[i].el.getHeight();
22150 //if (this.toolbars[i].footer) {
22151 // tbh += this.toolbars[i].footer.el.getHeight();
22159 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22160 ah -= 5; // knock a few pixes off for look..
22161 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22165 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22166 this.editorcore.onResize(ew,eh);
22171 * Toggles the editor between standard and source edit mode.
22172 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22174 toggleSourceEdit : function(sourceEditMode)
22176 this.editorcore.toggleSourceEdit(sourceEditMode);
22178 if(this.editorcore.sourceEditMode){
22179 Roo.log('editor - showing textarea');
22182 // Roo.log(this.syncValue());
22184 this.inputEl().removeClass(['hide', 'x-hidden']);
22185 this.inputEl().dom.removeAttribute('tabIndex');
22186 this.inputEl().focus();
22188 Roo.log('editor - hiding textarea');
22190 // Roo.log(this.pushValue());
22193 this.inputEl().addClass(['hide', 'x-hidden']);
22194 this.inputEl().dom.setAttribute('tabIndex', -1);
22195 //this.deferFocus();
22198 if(this.resizable){
22199 this.setSize(this.wrap.getSize());
22202 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22205 // private (for BoxComponent)
22206 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22208 // private (for BoxComponent)
22209 getResizeEl : function(){
22213 // private (for BoxComponent)
22214 getPositionEl : function(){
22219 initEvents : function(){
22220 this.originalValue = this.getValue();
22224 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22227 // markInvalid : Roo.emptyFn,
22229 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22232 // clearInvalid : Roo.emptyFn,
22234 setValue : function(v){
22235 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22236 this.editorcore.pushValue();
22241 deferFocus : function(){
22242 this.focus.defer(10, this);
22246 focus : function(){
22247 this.editorcore.focus();
22253 onDestroy : function(){
22259 for (var i =0; i < this.toolbars.length;i++) {
22260 // fixme - ask toolbars for heights?
22261 this.toolbars[i].onDestroy();
22264 this.wrap.dom.innerHTML = '';
22265 this.wrap.remove();
22270 onFirstFocus : function(){
22271 //Roo.log("onFirstFocus");
22272 this.editorcore.onFirstFocus();
22273 for (var i =0; i < this.toolbars.length;i++) {
22274 this.toolbars[i].onFirstFocus();
22280 syncValue : function()
22282 this.editorcore.syncValue();
22285 pushValue : function()
22287 this.editorcore.pushValue();
22291 // hide stuff that is not compatible
22305 * @event specialkey
22309 * @cfg {String} fieldClass @hide
22312 * @cfg {String} focusClass @hide
22315 * @cfg {String} autoCreate @hide
22318 * @cfg {String} inputType @hide
22321 * @cfg {String} invalidClass @hide
22324 * @cfg {String} invalidText @hide
22327 * @cfg {String} msgFx @hide
22330 * @cfg {String} validateOnBlur @hide
22339 Roo.namespace('Roo.bootstrap.htmleditor');
22341 * @class Roo.bootstrap.HtmlEditorToolbar1
22346 new Roo.bootstrap.HtmlEditor({
22349 new Roo.bootstrap.HtmlEditorToolbar1({
22350 disable : { fonts: 1 , format: 1, ..., ... , ...],
22356 * @cfg {Object} disable List of elements to disable..
22357 * @cfg {Array} btns List of additional buttons.
22361 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22364 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22367 Roo.apply(this, config);
22369 // default disabled, based on 'good practice'..
22370 this.disable = this.disable || {};
22371 Roo.applyIf(this.disable, {
22374 specialElements : true
22376 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22378 this.editor = config.editor;
22379 this.editorcore = config.editor.editorcore;
22381 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22383 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22384 // dont call parent... till later.
22386 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22391 editorcore : false,
22396 "h1","h2","h3","h4","h5","h6",
22398 "abbr", "acronym", "address", "cite", "samp", "var",
22402 onRender : function(ct, position)
22404 // Roo.log("Call onRender: " + this.xtype);
22406 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22408 this.el.dom.style.marginBottom = '0';
22410 var editorcore = this.editorcore;
22411 var editor= this.editor;
22414 var btn = function(id,cmd , toggle, handler){
22416 var event = toggle ? 'toggle' : 'click';
22421 xns: Roo.bootstrap,
22424 enableToggle:toggle !== false,
22426 pressed : toggle ? false : null,
22429 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22430 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22439 xns: Roo.bootstrap,
22440 glyphicon : 'font',
22444 xns: Roo.bootstrap,
22448 Roo.each(this.formats, function(f) {
22449 style.menu.items.push({
22451 xns: Roo.bootstrap,
22452 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22457 editorcore.insertTag(this.tagname);
22464 children.push(style);
22467 btn('bold',false,true);
22468 btn('italic',false,true);
22469 btn('align-left', 'justifyleft',true);
22470 btn('align-center', 'justifycenter',true);
22471 btn('align-right' , 'justifyright',true);
22472 btn('link', false, false, function(btn) {
22473 //Roo.log("create link?");
22474 var url = prompt(this.createLinkText, this.defaultLinkValue);
22475 if(url && url != 'http:/'+'/'){
22476 this.editorcore.relayCmd('createlink', url);
22479 btn('list','insertunorderedlist',true);
22480 btn('pencil', false,true, function(btn){
22483 this.toggleSourceEdit(btn.pressed);
22489 xns: Roo.bootstrap,
22494 xns: Roo.bootstrap,
22499 cog.menu.items.push({
22501 xns: Roo.bootstrap,
22502 html : Clean styles,
22507 editorcore.insertTag(this.tagname);
22516 this.xtype = 'NavSimplebar';
22518 for(var i=0;i< children.length;i++) {
22520 this.buttons.add(this.addxtypeChild(children[i]));
22524 editor.on('editorevent', this.updateToolbar, this);
22526 onBtnClick : function(id)
22528 this.editorcore.relayCmd(id);
22529 this.editorcore.focus();
22533 * Protected method that will not generally be called directly. It triggers
22534 * a toolbar update by reading the markup state of the current selection in the editor.
22536 updateToolbar: function(){
22538 if(!this.editorcore.activated){
22539 this.editor.onFirstFocus(); // is this neeed?
22543 var btns = this.buttons;
22544 var doc = this.editorcore.doc;
22545 btns.get('bold').setActive(doc.queryCommandState('bold'));
22546 btns.get('italic').setActive(doc.queryCommandState('italic'));
22547 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22549 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22550 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22551 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22553 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22554 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22557 var ans = this.editorcore.getAllAncestors();
22558 if (this.formatCombo) {
22561 var store = this.formatCombo.store;
22562 this.formatCombo.setValue("");
22563 for (var i =0; i < ans.length;i++) {
22564 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22566 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22574 // hides menus... - so this cant be on a menu...
22575 Roo.bootstrap.MenuMgr.hideAll();
22577 Roo.bootstrap.MenuMgr.hideAll();
22578 //this.editorsyncValue();
22580 onFirstFocus: function() {
22581 this.buttons.each(function(item){
22585 toggleSourceEdit : function(sourceEditMode){
22588 if(sourceEditMode){
22589 Roo.log("disabling buttons");
22590 this.buttons.each( function(item){
22591 if(item.cmd != 'pencil'){
22597 Roo.log("enabling buttons");
22598 if(this.editorcore.initialized){
22599 this.buttons.each( function(item){
22605 Roo.log("calling toggole on editor");
22606 // tell the editor that it's been pressed..
22607 this.editor.toggleSourceEdit(sourceEditMode);
22617 * @class Roo.bootstrap.Table.AbstractSelectionModel
22618 * @extends Roo.util.Observable
22619 * Abstract base class for grid SelectionModels. It provides the interface that should be
22620 * implemented by descendant classes. This class should not be directly instantiated.
22623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22624 this.locked = false;
22625 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22630 /** @ignore Called by the grid automatically. Do not call directly. */
22631 init : function(grid){
22637 * Locks the selections.
22640 this.locked = true;
22644 * Unlocks the selections.
22646 unlock : function(){
22647 this.locked = false;
22651 * Returns true if the selections are locked.
22652 * @return {Boolean}
22654 isLocked : function(){
22655 return this.locked;
22659 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22660 * @class Roo.bootstrap.Table.RowSelectionModel
22661 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22662 * It supports multiple selections and keyboard selection/navigation.
22664 * @param {Object} config
22667 Roo.bootstrap.Table.RowSelectionModel = function(config){
22668 Roo.apply(this, config);
22669 this.selections = new Roo.util.MixedCollection(false, function(o){
22674 this.lastActive = false;
22678 * @event selectionchange
22679 * Fires when the selection changes
22680 * @param {SelectionModel} this
22682 "selectionchange" : true,
22684 * @event afterselectionchange
22685 * Fires after the selection changes (eg. by key press or clicking)
22686 * @param {SelectionModel} this
22688 "afterselectionchange" : true,
22690 * @event beforerowselect
22691 * Fires when a row is selected being selected, return false to cancel.
22692 * @param {SelectionModel} this
22693 * @param {Number} rowIndex The selected index
22694 * @param {Boolean} keepExisting False if other selections will be cleared
22696 "beforerowselect" : true,
22699 * Fires when a row is selected.
22700 * @param {SelectionModel} this
22701 * @param {Number} rowIndex The selected index
22702 * @param {Roo.data.Record} r The record
22704 "rowselect" : true,
22706 * @event rowdeselect
22707 * Fires when a row is deselected.
22708 * @param {SelectionModel} this
22709 * @param {Number} rowIndex The selected index
22711 "rowdeselect" : true
22713 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22714 this.locked = false;
22717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22719 * @cfg {Boolean} singleSelect
22720 * True to allow selection of only one row at a time (defaults to false)
22722 singleSelect : false,
22725 initEvents : function()
22728 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22729 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22730 //}else{ // allow click to work like normal
22731 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22733 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22734 this.grid.on("rowclick", this.handleMouseDown, this);
22736 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22737 "up" : function(e){
22739 this.selectPrevious(e.shiftKey);
22740 }else if(this.last !== false && this.lastActive !== false){
22741 var last = this.last;
22742 this.selectRange(this.last, this.lastActive-1);
22743 this.grid.getView().focusRow(this.lastActive);
22744 if(last !== false){
22748 this.selectFirstRow();
22750 this.fireEvent("afterselectionchange", this);
22752 "down" : function(e){
22754 this.selectNext(e.shiftKey);
22755 }else if(this.last !== false && this.lastActive !== false){
22756 var last = this.last;
22757 this.selectRange(this.last, this.lastActive+1);
22758 this.grid.getView().focusRow(this.lastActive);
22759 if(last !== false){
22763 this.selectFirstRow();
22765 this.fireEvent("afterselectionchange", this);
22769 this.grid.store.on('load', function(){
22770 this.selections.clear();
22773 var view = this.grid.view;
22774 view.on("refresh", this.onRefresh, this);
22775 view.on("rowupdated", this.onRowUpdated, this);
22776 view.on("rowremoved", this.onRemove, this);
22781 onRefresh : function()
22783 var ds = this.grid.store, i, v = this.grid.view;
22784 var s = this.selections;
22785 s.each(function(r){
22786 if((i = ds.indexOfId(r.id)) != -1){
22795 onRemove : function(v, index, r){
22796 this.selections.remove(r);
22800 onRowUpdated : function(v, index, r){
22801 if(this.isSelected(r)){
22802 v.onRowSelect(index);
22808 * @param {Array} records The records to select
22809 * @param {Boolean} keepExisting (optional) True to keep existing selections
22811 selectRecords : function(records, keepExisting)
22814 this.clearSelections();
22816 var ds = this.grid.store;
22817 for(var i = 0, len = records.length; i < len; i++){
22818 this.selectRow(ds.indexOf(records[i]), true);
22823 * Gets the number of selected rows.
22826 getCount : function(){
22827 return this.selections.length;
22831 * Selects the first row in the grid.
22833 selectFirstRow : function(){
22838 * Select the last row.
22839 * @param {Boolean} keepExisting (optional) True to keep existing selections
22841 selectLastRow : function(keepExisting){
22842 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22843 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22847 * Selects the row immediately following the last selected row.
22848 * @param {Boolean} keepExisting (optional) True to keep existing selections
22850 selectNext : function(keepExisting)
22852 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22853 this.selectRow(this.last+1, keepExisting);
22854 this.grid.getView().focusRow(this.last);
22859 * Selects the row that precedes the last selected row.
22860 * @param {Boolean} keepExisting (optional) True to keep existing selections
22862 selectPrevious : function(keepExisting){
22864 this.selectRow(this.last-1, keepExisting);
22865 this.grid.getView().focusRow(this.last);
22870 * Returns the selected records
22871 * @return {Array} Array of selected records
22873 getSelections : function(){
22874 return [].concat(this.selections.items);
22878 * Returns the first selected record.
22881 getSelected : function(){
22882 return this.selections.itemAt(0);
22887 * Clears all selections.
22889 clearSelections : function(fast)
22895 var ds = this.grid.store;
22896 var s = this.selections;
22897 s.each(function(r){
22898 this.deselectRow(ds.indexOfId(r.id));
22902 this.selections.clear();
22909 * Selects all rows.
22911 selectAll : function(){
22915 this.selections.clear();
22916 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22917 this.selectRow(i, true);
22922 * Returns True if there is a selection.
22923 * @return {Boolean}
22925 hasSelection : function(){
22926 return this.selections.length > 0;
22930 * Returns True if the specified row is selected.
22931 * @param {Number/Record} record The record or index of the record to check
22932 * @return {Boolean}
22934 isSelected : function(index){
22935 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22936 return (r && this.selections.key(r.id) ? true : false);
22940 * Returns True if the specified record id is selected.
22941 * @param {String} id The id of record to check
22942 * @return {Boolean}
22944 isIdSelected : function(id){
22945 return (this.selections.key(id) ? true : false);
22950 handleMouseDBClick : function(e, t){
22954 handleMouseDown : function(e, t)
22956 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22957 if(this.isLocked() || rowIndex < 0 ){
22960 if(e.shiftKey && this.last !== false){
22961 var last = this.last;
22962 this.selectRange(last, rowIndex, e.ctrlKey);
22963 this.last = last; // reset the last
22967 var isSelected = this.isSelected(rowIndex);
22968 //Roo.log("select row:" + rowIndex);
22970 this.deselectRow(rowIndex);
22972 this.selectRow(rowIndex, true);
22976 if(e.button !== 0 && isSelected){
22977 alert('rowIndex 2: ' + rowIndex);
22978 view.focusRow(rowIndex);
22979 }else if(e.ctrlKey && isSelected){
22980 this.deselectRow(rowIndex);
22981 }else if(!isSelected){
22982 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22983 view.focusRow(rowIndex);
22987 this.fireEvent("afterselectionchange", this);
22990 handleDragableRowClick : function(grid, rowIndex, e)
22992 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22993 this.selectRow(rowIndex, false);
22994 grid.view.focusRow(rowIndex);
22995 this.fireEvent("afterselectionchange", this);
23000 * Selects multiple rows.
23001 * @param {Array} rows Array of the indexes of the row to select
23002 * @param {Boolean} keepExisting (optional) True to keep existing selections
23004 selectRows : function(rows, keepExisting){
23006 this.clearSelections();
23008 for(var i = 0, len = rows.length; i < len; i++){
23009 this.selectRow(rows[i], true);
23014 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23015 * @param {Number} startRow The index of the first row in the range
23016 * @param {Number} endRow The index of the last row in the range
23017 * @param {Boolean} keepExisting (optional) True to retain existing selections
23019 selectRange : function(startRow, endRow, keepExisting){
23024 this.clearSelections();
23026 if(startRow <= endRow){
23027 for(var i = startRow; i <= endRow; i++){
23028 this.selectRow(i, true);
23031 for(var i = startRow; i >= endRow; i--){
23032 this.selectRow(i, true);
23038 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23039 * @param {Number} startRow The index of the first row in the range
23040 * @param {Number} endRow The index of the last row in the range
23042 deselectRange : function(startRow, endRow, preventViewNotify){
23046 for(var i = startRow; i <= endRow; i++){
23047 this.deselectRow(i, preventViewNotify);
23053 * @param {Number} row The index of the row to select
23054 * @param {Boolean} keepExisting (optional) True to keep existing selections
23056 selectRow : function(index, keepExisting, preventViewNotify)
23058 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23061 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23062 if(!keepExisting || this.singleSelect){
23063 this.clearSelections();
23066 var r = this.grid.store.getAt(index);
23067 //console.log('selectRow - record id :' + r.id);
23069 this.selections.add(r);
23070 this.last = this.lastActive = index;
23071 if(!preventViewNotify){
23072 var proxy = new Roo.Element(
23073 this.grid.getRowDom(index)
23075 proxy.addClass('bg-info info');
23077 this.fireEvent("rowselect", this, index, r);
23078 this.fireEvent("selectionchange", this);
23084 * @param {Number} row The index of the row to deselect
23086 deselectRow : function(index, preventViewNotify)
23091 if(this.last == index){
23094 if(this.lastActive == index){
23095 this.lastActive = false;
23098 var r = this.grid.store.getAt(index);
23103 this.selections.remove(r);
23104 //.console.log('deselectRow - record id :' + r.id);
23105 if(!preventViewNotify){
23107 var proxy = new Roo.Element(
23108 this.grid.getRowDom(index)
23110 proxy.removeClass('bg-info info');
23112 this.fireEvent("rowdeselect", this, index);
23113 this.fireEvent("selectionchange", this);
23117 restoreLast : function(){
23119 this.last = this._last;
23124 acceptsNav : function(row, col, cm){
23125 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23129 onEditorKey : function(field, e){
23130 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23135 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23137 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23139 }else if(k == e.ENTER && !e.ctrlKey){
23143 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23145 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23147 }else if(k == e.ESC){
23151 g.startEditing(newCell[0], newCell[1]);
23157 * Ext JS Library 1.1.1
23158 * Copyright(c) 2006-2007, Ext JS, LLC.
23160 * Originally Released Under LGPL - original licence link has changed is not relivant.
23163 * <script type="text/javascript">
23167 * @class Roo.bootstrap.PagingToolbar
23168 * @extends Roo.bootstrap.NavSimplebar
23169 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23171 * Create a new PagingToolbar
23172 * @param {Object} config The config object
23173 * @param {Roo.data.Store} store
23175 Roo.bootstrap.PagingToolbar = function(config)
23177 // old args format still supported... - xtype is prefered..
23178 // created from xtype...
23180 this.ds = config.dataSource;
23182 if (config.store && !this.ds) {
23183 this.store= Roo.factory(config.store, Roo.data);
23184 this.ds = this.store;
23185 this.ds.xmodule = this.xmodule || false;
23188 this.toolbarItems = [];
23189 if (config.items) {
23190 this.toolbarItems = config.items;
23193 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23198 this.bind(this.ds);
23201 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23205 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23207 * @cfg {Roo.data.Store} dataSource
23208 * The underlying data store providing the paged data
23211 * @cfg {String/HTMLElement/Element} container
23212 * container The id or element that will contain the toolbar
23215 * @cfg {Boolean} displayInfo
23216 * True to display the displayMsg (defaults to false)
23219 * @cfg {Number} pageSize
23220 * The number of records to display per page (defaults to 20)
23224 * @cfg {String} displayMsg
23225 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23227 displayMsg : 'Displaying {0} - {1} of {2}',
23229 * @cfg {String} emptyMsg
23230 * The message to display when no records are found (defaults to "No data to display")
23232 emptyMsg : 'No data to display',
23234 * Customizable piece of the default paging text (defaults to "Page")
23237 beforePageText : "Page",
23239 * Customizable piece of the default paging text (defaults to "of %0")
23242 afterPageText : "of {0}",
23244 * Customizable piece of the default paging text (defaults to "First Page")
23247 firstText : "First Page",
23249 * Customizable piece of the default paging text (defaults to "Previous Page")
23252 prevText : "Previous Page",
23254 * Customizable piece of the default paging text (defaults to "Next Page")
23257 nextText : "Next Page",
23259 * Customizable piece of the default paging text (defaults to "Last Page")
23262 lastText : "Last Page",
23264 * Customizable piece of the default paging text (defaults to "Refresh")
23267 refreshText : "Refresh",
23271 onRender : function(ct, position)
23273 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23274 this.navgroup.parentId = this.id;
23275 this.navgroup.onRender(this.el, null);
23276 // add the buttons to the navgroup
23278 if(this.displayInfo){
23279 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23280 this.displayEl = this.el.select('.x-paging-info', true).first();
23281 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23282 // this.displayEl = navel.el.select('span',true).first();
23288 Roo.each(_this.buttons, function(e){ // this might need to use render????
23289 Roo.factory(e).onRender(_this.el, null);
23293 Roo.each(_this.toolbarItems, function(e) {
23294 _this.navgroup.addItem(e);
23298 this.first = this.navgroup.addItem({
23299 tooltip: this.firstText,
23301 icon : 'fa fa-backward',
23303 preventDefault: true,
23304 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23307 this.prev = this.navgroup.addItem({
23308 tooltip: this.prevText,
23310 icon : 'fa fa-step-backward',
23312 preventDefault: true,
23313 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23315 //this.addSeparator();
23318 var field = this.navgroup.addItem( {
23320 cls : 'x-paging-position',
23322 html : this.beforePageText +
23323 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23324 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23327 this.field = field.el.select('input', true).first();
23328 this.field.on("keydown", this.onPagingKeydown, this);
23329 this.field.on("focus", function(){this.dom.select();});
23332 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23333 //this.field.setHeight(18);
23334 //this.addSeparator();
23335 this.next = this.navgroup.addItem({
23336 tooltip: this.nextText,
23338 html : ' <i class="fa fa-step-forward">',
23340 preventDefault: true,
23341 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23343 this.last = this.navgroup.addItem({
23344 tooltip: this.lastText,
23345 icon : 'fa fa-forward',
23348 preventDefault: true,
23349 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23351 //this.addSeparator();
23352 this.loading = this.navgroup.addItem({
23353 tooltip: this.refreshText,
23354 icon: 'fa fa-refresh',
23355 preventDefault: true,
23356 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23362 updateInfo : function(){
23363 if(this.displayEl){
23364 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23365 var msg = count == 0 ?
23369 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23371 this.displayEl.update(msg);
23376 onLoad : function(ds, r, o){
23377 this.cursor = o.params ? o.params.start : 0;
23378 var d = this.getPageData(),
23382 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23383 this.field.dom.value = ap;
23384 this.first.setDisabled(ap == 1);
23385 this.prev.setDisabled(ap == 1);
23386 this.next.setDisabled(ap == ps);
23387 this.last.setDisabled(ap == ps);
23388 this.loading.enable();
23393 getPageData : function(){
23394 var total = this.ds.getTotalCount();
23397 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23398 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23403 onLoadError : function(){
23404 this.loading.enable();
23408 onPagingKeydown : function(e){
23409 var k = e.getKey();
23410 var d = this.getPageData();
23412 var v = this.field.dom.value, pageNum;
23413 if(!v || isNaN(pageNum = parseInt(v, 10))){
23414 this.field.dom.value = d.activePage;
23417 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23418 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23421 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))
23423 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23424 this.field.dom.value = pageNum;
23425 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23428 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23430 var v = this.field.dom.value, pageNum;
23431 var increment = (e.shiftKey) ? 10 : 1;
23432 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23435 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23436 this.field.dom.value = d.activePage;
23439 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23441 this.field.dom.value = parseInt(v, 10) + increment;
23442 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23443 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23450 beforeLoad : function(){
23452 this.loading.disable();
23457 onClick : function(which){
23466 ds.load({params:{start: 0, limit: this.pageSize}});
23469 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23472 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23475 var total = ds.getTotalCount();
23476 var extra = total % this.pageSize;
23477 var lastStart = extra ? (total - extra) : total-this.pageSize;
23478 ds.load({params:{start: lastStart, limit: this.pageSize}});
23481 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23487 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23488 * @param {Roo.data.Store} store The data store to unbind
23490 unbind : function(ds){
23491 ds.un("beforeload", this.beforeLoad, this);
23492 ds.un("load", this.onLoad, this);
23493 ds.un("loadexception", this.onLoadError, this);
23494 ds.un("remove", this.updateInfo, this);
23495 ds.un("add", this.updateInfo, this);
23496 this.ds = undefined;
23500 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23501 * @param {Roo.data.Store} store The data store to bind
23503 bind : function(ds){
23504 ds.on("beforeload", this.beforeLoad, this);
23505 ds.on("load", this.onLoad, this);
23506 ds.on("loadexception", this.onLoadError, this);
23507 ds.on("remove", this.updateInfo, this);
23508 ds.on("add", this.updateInfo, this);
23519 * @class Roo.bootstrap.MessageBar
23520 * @extends Roo.bootstrap.Component
23521 * Bootstrap MessageBar class
23522 * @cfg {String} html contents of the MessageBar
23523 * @cfg {String} weight (info | success | warning | danger) default info
23524 * @cfg {String} beforeClass insert the bar before the given class
23525 * @cfg {Boolean} closable (true | false) default false
23526 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23529 * Create a new Element
23530 * @param {Object} config The config object
23533 Roo.bootstrap.MessageBar = function(config){
23534 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23537 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23543 beforeClass: 'bootstrap-sticky-wrap',
23545 getAutoCreate : function(){
23549 cls: 'alert alert-dismissable alert-' + this.weight,
23554 html: this.html || ''
23560 cfg.cls += ' alert-messages-fixed';
23574 onRender : function(ct, position)
23576 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23579 var cfg = Roo.apply({}, this.getAutoCreate());
23583 cfg.cls += ' ' + this.cls;
23586 cfg.style = this.style;
23588 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23590 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23593 this.el.select('>button.close').on('click', this.hide, this);
23599 if (!this.rendered) {
23605 this.fireEvent('show', this);
23611 if (!this.rendered) {
23617 this.fireEvent('hide', this);
23620 update : function()
23622 // var e = this.el.dom.firstChild;
23624 // if(this.closable){
23625 // e = e.nextSibling;
23628 // e.data = this.html || '';
23630 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23646 * @class Roo.bootstrap.Graph
23647 * @extends Roo.bootstrap.Component
23648 * Bootstrap Graph class
23652 @cfg {String} graphtype bar | vbar | pie
23653 @cfg {number} g_x coodinator | centre x (pie)
23654 @cfg {number} g_y coodinator | centre y (pie)
23655 @cfg {number} g_r radius (pie)
23656 @cfg {number} g_height height of the chart (respected by all elements in the set)
23657 @cfg {number} g_width width of the chart (respected by all elements in the set)
23658 @cfg {Object} title The title of the chart
23661 -opts (object) options for the chart
23663 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23664 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23666 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.
23667 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23669 o stretch (boolean)
23671 -opts (object) options for the pie
23674 o startAngle (number)
23675 o endAngle (number)
23679 * Create a new Input
23680 * @param {Object} config The config object
23683 Roo.bootstrap.Graph = function(config){
23684 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23690 * The img click event for the img.
23691 * @param {Roo.EventObject} e
23697 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23708 //g_colors: this.colors,
23715 getAutoCreate : function(){
23726 onRender : function(ct,position){
23729 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23731 if (typeof(Raphael) == 'undefined') {
23732 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23736 this.raphael = Raphael(this.el.dom);
23738 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23739 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23740 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23741 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23743 r.text(160, 10, "Single Series Chart").attr(txtattr);
23744 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23745 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23746 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23748 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23749 r.barchart(330, 10, 300, 220, data1);
23750 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23751 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23754 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23755 // r.barchart(30, 30, 560, 250, xdata, {
23756 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23757 // axis : "0 0 1 1",
23758 // axisxlabels : xdata
23759 // //yvalues : cols,
23762 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23764 // this.load(null,xdata,{
23765 // axis : "0 0 1 1",
23766 // axisxlabels : xdata
23771 load : function(graphtype,xdata,opts)
23773 this.raphael.clear();
23775 graphtype = this.graphtype;
23780 var r = this.raphael,
23781 fin = function () {
23782 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23784 fout = function () {
23785 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23787 pfin = function() {
23788 this.sector.stop();
23789 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23792 this.label[0].stop();
23793 this.label[0].attr({ r: 7.5 });
23794 this.label[1].attr({ "font-weight": 800 });
23797 pfout = function() {
23798 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23801 this.label[0].animate({ r: 5 }, 500, "bounce");
23802 this.label[1].attr({ "font-weight": 400 });
23808 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23811 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23814 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23815 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23817 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23824 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23829 setTitle: function(o)
23834 initEvents: function() {
23837 this.el.on('click', this.onClick, this);
23841 onClick : function(e)
23843 Roo.log('img onclick');
23844 this.fireEvent('click', this, e);
23856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23859 * @class Roo.bootstrap.dash.NumberBox
23860 * @extends Roo.bootstrap.Component
23861 * Bootstrap NumberBox class
23862 * @cfg {String} headline Box headline
23863 * @cfg {String} content Box content
23864 * @cfg {String} icon Box icon
23865 * @cfg {String} footer Footer text
23866 * @cfg {String} fhref Footer href
23869 * Create a new NumberBox
23870 * @param {Object} config The config object
23874 Roo.bootstrap.dash.NumberBox = function(config){
23875 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23879 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23888 getAutoCreate : function(){
23892 cls : 'small-box ',
23900 cls : 'roo-headline',
23901 html : this.headline
23905 cls : 'roo-content',
23906 html : this.content
23920 cls : 'ion ' + this.icon
23929 cls : 'small-box-footer',
23930 href : this.fhref || '#',
23934 cfg.cn.push(footer);
23941 onRender : function(ct,position){
23942 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23949 setHeadline: function (value)
23951 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23954 setFooter: function (value, href)
23956 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23959 this.el.select('a.small-box-footer',true).first().attr('href', href);
23964 setContent: function (value)
23966 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23969 initEvents: function()
23983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23986 * @class Roo.bootstrap.dash.TabBox
23987 * @extends Roo.bootstrap.Component
23988 * Bootstrap TabBox class
23989 * @cfg {String} title Title of the TabBox
23990 * @cfg {String} icon Icon of the TabBox
23991 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23992 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23995 * Create a new TabBox
23996 * @param {Object} config The config object
24000 Roo.bootstrap.dash.TabBox = function(config){
24001 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24006 * When a pane is added
24007 * @param {Roo.bootstrap.dash.TabPane} pane
24011 * @event activatepane
24012 * When a pane is activated
24013 * @param {Roo.bootstrap.dash.TabPane} pane
24015 "activatepane" : true
24023 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24028 tabScrollable : false,
24030 getChildContainer : function()
24032 return this.el.select('.tab-content', true).first();
24035 getAutoCreate : function(){
24039 cls: 'pull-left header',
24047 cls: 'fa ' + this.icon
24053 cls: 'nav nav-tabs pull-right',
24059 if(this.tabScrollable){
24066 cls: 'nav nav-tabs pull-right',
24077 cls: 'nav-tabs-custom',
24082 cls: 'tab-content no-padding',
24090 initEvents : function()
24092 //Roo.log('add add pane handler');
24093 this.on('addpane', this.onAddPane, this);
24096 * Updates the box title
24097 * @param {String} html to set the title to.
24099 setTitle : function(value)
24101 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24103 onAddPane : function(pane)
24105 this.panes.push(pane);
24106 //Roo.log('addpane');
24108 // tabs are rendere left to right..
24109 if(!this.showtabs){
24113 var ctr = this.el.select('.nav-tabs', true).first();
24116 var existing = ctr.select('.nav-tab',true);
24117 var qty = existing.getCount();;
24120 var tab = ctr.createChild({
24122 cls : 'nav-tab' + (qty ? '' : ' active'),
24130 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24133 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24135 pane.el.addClass('active');
24140 onTabClick : function(ev,un,ob,pane)
24142 //Roo.log('tab - prev default');
24143 ev.preventDefault();
24146 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24147 pane.tab.addClass('active');
24148 //Roo.log(pane.title);
24149 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24150 // technically we should have a deactivate event.. but maybe add later.
24151 // and it should not de-activate the selected tab...
24152 this.fireEvent('activatepane', pane);
24153 pane.el.addClass('active');
24154 pane.fireEvent('activate');
24159 getActivePane : function()
24162 Roo.each(this.panes, function(p) {
24163 if(p.el.hasClass('active')){
24184 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24186 * @class Roo.bootstrap.TabPane
24187 * @extends Roo.bootstrap.Component
24188 * Bootstrap TabPane class
24189 * @cfg {Boolean} active (false | true) Default false
24190 * @cfg {String} title title of panel
24194 * Create a new TabPane
24195 * @param {Object} config The config object
24198 Roo.bootstrap.dash.TabPane = function(config){
24199 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24205 * When a pane is activated
24206 * @param {Roo.bootstrap.dash.TabPane} pane
24213 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24218 // the tabBox that this is attached to.
24221 getAutoCreate : function()
24229 cfg.cls += ' active';
24234 initEvents : function()
24236 //Roo.log('trigger add pane handler');
24237 this.parent().fireEvent('addpane', this)
24241 * Updates the tab title
24242 * @param {String} html to set the title to.
24244 setTitle: function(str)
24250 this.tab.select('a', true).first().dom.innerHTML = str;
24267 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24270 * @class Roo.bootstrap.menu.Menu
24271 * @extends Roo.bootstrap.Component
24272 * Bootstrap Menu class - container for Menu
24273 * @cfg {String} html Text of the menu
24274 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24275 * @cfg {String} icon Font awesome icon
24276 * @cfg {String} pos Menu align to (top | bottom) default bottom
24280 * Create a new Menu
24281 * @param {Object} config The config object
24285 Roo.bootstrap.menu.Menu = function(config){
24286 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24290 * @event beforeshow
24291 * Fires before this menu is displayed
24292 * @param {Roo.bootstrap.menu.Menu} this
24296 * @event beforehide
24297 * Fires before this menu is hidden
24298 * @param {Roo.bootstrap.menu.Menu} this
24303 * Fires after this menu is displayed
24304 * @param {Roo.bootstrap.menu.Menu} this
24309 * Fires after this menu is hidden
24310 * @param {Roo.bootstrap.menu.Menu} this
24315 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24316 * @param {Roo.bootstrap.menu.Menu} this
24317 * @param {Roo.EventObject} e
24324 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24328 weight : 'default',
24333 getChildContainer : function() {
24334 if(this.isSubMenu){
24338 return this.el.select('ul.dropdown-menu', true).first();
24341 getAutoCreate : function()
24346 cls : 'roo-menu-text',
24354 cls : 'fa ' + this.icon
24365 cls : 'dropdown-button btn btn-' + this.weight,
24370 cls : 'dropdown-toggle btn btn-' + this.weight,
24380 cls : 'dropdown-menu'
24386 if(this.pos == 'top'){
24387 cfg.cls += ' dropup';
24390 if(this.isSubMenu){
24393 cls : 'dropdown-menu'
24400 onRender : function(ct, position)
24402 this.isSubMenu = ct.hasClass('dropdown-submenu');
24404 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24407 initEvents : function()
24409 if(this.isSubMenu){
24413 this.hidden = true;
24415 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24416 this.triggerEl.on('click', this.onTriggerPress, this);
24418 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24419 this.buttonEl.on('click', this.onClick, this);
24425 if(this.isSubMenu){
24429 return this.el.select('ul.dropdown-menu', true).first();
24432 onClick : function(e)
24434 this.fireEvent("click", this, e);
24437 onTriggerPress : function(e)
24439 if (this.isVisible()) {
24446 isVisible : function(){
24447 return !this.hidden;
24452 this.fireEvent("beforeshow", this);
24454 this.hidden = false;
24455 this.el.addClass('open');
24457 Roo.get(document).on("mouseup", this.onMouseUp, this);
24459 this.fireEvent("show", this);
24466 this.fireEvent("beforehide", this);
24468 this.hidden = true;
24469 this.el.removeClass('open');
24471 Roo.get(document).un("mouseup", this.onMouseUp);
24473 this.fireEvent("hide", this);
24476 onMouseUp : function()
24490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24493 * @class Roo.bootstrap.menu.Item
24494 * @extends Roo.bootstrap.Component
24495 * Bootstrap MenuItem class
24496 * @cfg {Boolean} submenu (true | false) default false
24497 * @cfg {String} html text of the item
24498 * @cfg {String} href the link
24499 * @cfg {Boolean} disable (true | false) default false
24500 * @cfg {Boolean} preventDefault (true | false) default true
24501 * @cfg {String} icon Font awesome icon
24502 * @cfg {String} pos Submenu align to (left | right) default right
24506 * Create a new Item
24507 * @param {Object} config The config object
24511 Roo.bootstrap.menu.Item = function(config){
24512 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24516 * Fires when the mouse is hovering over this menu
24517 * @param {Roo.bootstrap.menu.Item} this
24518 * @param {Roo.EventObject} e
24523 * Fires when the mouse exits this menu
24524 * @param {Roo.bootstrap.menu.Item} this
24525 * @param {Roo.EventObject} e
24531 * The raw click event for the entire grid.
24532 * @param {Roo.EventObject} e
24538 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24543 preventDefault: true,
24548 getAutoCreate : function()
24553 cls : 'roo-menu-item-text',
24561 cls : 'fa ' + this.icon
24570 href : this.href || '#',
24577 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24581 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24583 if(this.pos == 'left'){
24584 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24591 initEvents : function()
24593 this.el.on('mouseover', this.onMouseOver, this);
24594 this.el.on('mouseout', this.onMouseOut, this);
24596 this.el.select('a', true).first().on('click', this.onClick, this);
24600 onClick : function(e)
24602 if(this.preventDefault){
24603 e.preventDefault();
24606 this.fireEvent("click", this, e);
24609 onMouseOver : function(e)
24611 if(this.submenu && this.pos == 'left'){
24612 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24615 this.fireEvent("mouseover", this, e);
24618 onMouseOut : function(e)
24620 this.fireEvent("mouseout", this, e);
24632 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24635 * @class Roo.bootstrap.menu.Separator
24636 * @extends Roo.bootstrap.Component
24637 * Bootstrap Separator class
24640 * Create a new Separator
24641 * @param {Object} config The config object
24645 Roo.bootstrap.menu.Separator = function(config){
24646 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24649 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24651 getAutoCreate : function(){
24672 * @class Roo.bootstrap.Tooltip
24673 * Bootstrap Tooltip class
24674 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24675 * to determine which dom element triggers the tooltip.
24677 * It needs to add support for additional attributes like tooltip-position
24680 * Create a new Toolti
24681 * @param {Object} config The config object
24684 Roo.bootstrap.Tooltip = function(config){
24685 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24688 Roo.apply(Roo.bootstrap.Tooltip, {
24690 * @function init initialize tooltip monitoring.
24694 currentTip : false,
24695 currentRegion : false,
24701 Roo.get(document).on('mouseover', this.enter ,this);
24702 Roo.get(document).on('mouseout', this.leave, this);
24705 this.currentTip = new Roo.bootstrap.Tooltip();
24708 enter : function(ev)
24710 var dom = ev.getTarget();
24712 //Roo.log(['enter',dom]);
24713 var el = Roo.fly(dom);
24714 if (this.currentEl) {
24716 //Roo.log(this.currentEl);
24717 //Roo.log(this.currentEl.contains(dom));
24718 if (this.currentEl == el) {
24721 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24727 if (this.currentTip.el) {
24728 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24732 if(!el || el.dom == document){
24738 // you can not look for children, as if el is the body.. then everythign is the child..
24739 if (!el.attr('tooltip')) { //
24740 if (!el.select("[tooltip]").elements.length) {
24743 // is the mouse over this child...?
24744 bindEl = el.select("[tooltip]").first();
24745 var xy = ev.getXY();
24746 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24747 //Roo.log("not in region.");
24750 //Roo.log("child element over..");
24753 this.currentEl = bindEl;
24754 this.currentTip.bind(bindEl);
24755 this.currentRegion = Roo.lib.Region.getRegion(dom);
24756 this.currentTip.enter();
24759 leave : function(ev)
24761 var dom = ev.getTarget();
24762 //Roo.log(['leave',dom]);
24763 if (!this.currentEl) {
24768 if (dom != this.currentEl.dom) {
24771 var xy = ev.getXY();
24772 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24775 // only activate leave if mouse cursor is outside... bounding box..
24780 if (this.currentTip) {
24781 this.currentTip.leave();
24783 //Roo.log('clear currentEl');
24784 this.currentEl = false;
24789 'left' : ['r-l', [-2,0], 'right'],
24790 'right' : ['l-r', [2,0], 'left'],
24791 'bottom' : ['t-b', [0,2], 'top'],
24792 'top' : [ 'b-t', [0,-2], 'bottom']
24798 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24803 delay : null, // can be { show : 300 , hide: 500}
24807 hoverState : null, //???
24809 placement : 'bottom',
24811 getAutoCreate : function(){
24818 cls : 'tooltip-arrow'
24821 cls : 'tooltip-inner'
24828 bind : function(el)
24834 enter : function () {
24836 if (this.timeout != null) {
24837 clearTimeout(this.timeout);
24840 this.hoverState = 'in';
24841 //Roo.log("enter - show");
24842 if (!this.delay || !this.delay.show) {
24847 this.timeout = setTimeout(function () {
24848 if (_t.hoverState == 'in') {
24851 }, this.delay.show);
24855 clearTimeout(this.timeout);
24857 this.hoverState = 'out';
24858 if (!this.delay || !this.delay.hide) {
24864 this.timeout = setTimeout(function () {
24865 //Roo.log("leave - timeout");
24867 if (_t.hoverState == 'out') {
24869 Roo.bootstrap.Tooltip.currentEl = false;
24877 this.render(document.body);
24880 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24882 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24884 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24886 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24888 var placement = typeof this.placement == 'function' ?
24889 this.placement.call(this, this.el, on_el) :
24892 var autoToken = /\s?auto?\s?/i;
24893 var autoPlace = autoToken.test(placement);
24895 placement = placement.replace(autoToken, '') || 'top';
24899 //this.el.setXY([0,0]);
24901 //this.el.dom.style.display='block';
24903 //this.el.appendTo(on_el);
24905 var p = this.getPosition();
24906 var box = this.el.getBox();
24912 var align = Roo.bootstrap.Tooltip.alignment[placement];
24914 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24916 if(placement == 'top' || placement == 'bottom'){
24918 placement = 'right';
24921 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24922 placement = 'left';
24925 var scroll = Roo.select('body', true).first().getScroll();
24927 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24933 align = Roo.bootstrap.Tooltip.alignment[placement];
24935 this.el.alignTo(this.bindEl, align[0],align[1]);
24936 //var arrow = this.el.select('.arrow',true).first();
24937 //arrow.set(align[2],
24939 this.el.addClass(placement);
24941 this.el.addClass('in fade');
24943 this.hoverState = null;
24945 if (this.el.hasClass('fade')) {
24956 //this.el.setXY([0,0]);
24957 this.el.removeClass('in');
24973 * @class Roo.bootstrap.LocationPicker
24974 * @extends Roo.bootstrap.Component
24975 * Bootstrap LocationPicker class
24976 * @cfg {Number} latitude Position when init default 0
24977 * @cfg {Number} longitude Position when init default 0
24978 * @cfg {Number} zoom default 15
24979 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24980 * @cfg {Boolean} mapTypeControl default false
24981 * @cfg {Boolean} disableDoubleClickZoom default false
24982 * @cfg {Boolean} scrollwheel default true
24983 * @cfg {Boolean} streetViewControl default false
24984 * @cfg {Number} radius default 0
24985 * @cfg {String} locationName
24986 * @cfg {Boolean} draggable default true
24987 * @cfg {Boolean} enableAutocomplete default false
24988 * @cfg {Boolean} enableReverseGeocode default true
24989 * @cfg {String} markerTitle
24992 * Create a new LocationPicker
24993 * @param {Object} config The config object
24997 Roo.bootstrap.LocationPicker = function(config){
24999 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25004 * Fires when the picker initialized.
25005 * @param {Roo.bootstrap.LocationPicker} this
25006 * @param {Google Location} location
25010 * @event positionchanged
25011 * Fires when the picker position changed.
25012 * @param {Roo.bootstrap.LocationPicker} this
25013 * @param {Google Location} location
25015 positionchanged : true,
25018 * Fires when the map resize.
25019 * @param {Roo.bootstrap.LocationPicker} this
25024 * Fires when the map show.
25025 * @param {Roo.bootstrap.LocationPicker} this
25030 * Fires when the map hide.
25031 * @param {Roo.bootstrap.LocationPicker} this
25036 * Fires when click the map.
25037 * @param {Roo.bootstrap.LocationPicker} this
25038 * @param {Map event} e
25042 * @event mapRightClick
25043 * Fires when right click the map.
25044 * @param {Roo.bootstrap.LocationPicker} this
25045 * @param {Map event} e
25047 mapRightClick : true,
25049 * @event markerClick
25050 * Fires when click the marker.
25051 * @param {Roo.bootstrap.LocationPicker} this
25052 * @param {Map event} e
25054 markerClick : true,
25056 * @event markerRightClick
25057 * Fires when right click the marker.
25058 * @param {Roo.bootstrap.LocationPicker} this
25059 * @param {Map event} e
25061 markerRightClick : true,
25063 * @event OverlayViewDraw
25064 * Fires when OverlayView Draw
25065 * @param {Roo.bootstrap.LocationPicker} this
25067 OverlayViewDraw : true,
25069 * @event OverlayViewOnAdd
25070 * Fires when OverlayView Draw
25071 * @param {Roo.bootstrap.LocationPicker} this
25073 OverlayViewOnAdd : true,
25075 * @event OverlayViewOnRemove
25076 * Fires when OverlayView Draw
25077 * @param {Roo.bootstrap.LocationPicker} this
25079 OverlayViewOnRemove : true,
25081 * @event OverlayViewShow
25082 * Fires when OverlayView Draw
25083 * @param {Roo.bootstrap.LocationPicker} this
25084 * @param {Pixel} cpx
25086 OverlayViewShow : true,
25088 * @event OverlayViewHide
25089 * Fires when OverlayView Draw
25090 * @param {Roo.bootstrap.LocationPicker} this
25092 OverlayViewHide : true,
25094 * @event loadexception
25095 * Fires when load google lib failed.
25096 * @param {Roo.bootstrap.LocationPicker} this
25098 loadexception : true
25103 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25105 gMapContext: false,
25111 mapTypeControl: false,
25112 disableDoubleClickZoom: false,
25114 streetViewControl: false,
25118 enableAutocomplete: false,
25119 enableReverseGeocode: true,
25122 getAutoCreate: function()
25127 cls: 'roo-location-picker'
25133 initEvents: function(ct, position)
25135 if(!this.el.getWidth() || this.isApplied()){
25139 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25144 initial: function()
25146 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25147 this.fireEvent('loadexception', this);
25151 if(!this.mapTypeId){
25152 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25155 this.gMapContext = this.GMapContext();
25157 this.initOverlayView();
25159 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25163 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25164 _this.setPosition(_this.gMapContext.marker.position);
25167 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25168 _this.fireEvent('mapClick', this, event);
25172 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25173 _this.fireEvent('mapRightClick', this, event);
25177 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25178 _this.fireEvent('markerClick', this, event);
25182 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25183 _this.fireEvent('markerRightClick', this, event);
25187 this.setPosition(this.gMapContext.location);
25189 this.fireEvent('initial', this, this.gMapContext.location);
25192 initOverlayView: function()
25196 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25200 _this.fireEvent('OverlayViewDraw', _this);
25205 _this.fireEvent('OverlayViewOnAdd', _this);
25208 onRemove: function()
25210 _this.fireEvent('OverlayViewOnRemove', _this);
25213 show: function(cpx)
25215 _this.fireEvent('OverlayViewShow', _this, cpx);
25220 _this.fireEvent('OverlayViewHide', _this);
25226 fromLatLngToContainerPixel: function(event)
25228 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25231 isApplied: function()
25233 return this.getGmapContext() == false ? false : true;
25236 getGmapContext: function()
25238 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25241 GMapContext: function()
25243 var position = new google.maps.LatLng(this.latitude, this.longitude);
25245 var _map = new google.maps.Map(this.el.dom, {
25248 mapTypeId: this.mapTypeId,
25249 mapTypeControl: this.mapTypeControl,
25250 disableDoubleClickZoom: this.disableDoubleClickZoom,
25251 scrollwheel: this.scrollwheel,
25252 streetViewControl: this.streetViewControl,
25253 locationName: this.locationName,
25254 draggable: this.draggable,
25255 enableAutocomplete: this.enableAutocomplete,
25256 enableReverseGeocode: this.enableReverseGeocode
25259 var _marker = new google.maps.Marker({
25260 position: position,
25262 title: this.markerTitle,
25263 draggable: this.draggable
25270 location: position,
25271 radius: this.radius,
25272 locationName: this.locationName,
25273 addressComponents: {
25274 formatted_address: null,
25275 addressLine1: null,
25276 addressLine2: null,
25278 streetNumber: null,
25282 stateOrProvince: null
25285 domContainer: this.el.dom,
25286 geodecoder: new google.maps.Geocoder()
25290 drawCircle: function(center, radius, options)
25292 if (this.gMapContext.circle != null) {
25293 this.gMapContext.circle.setMap(null);
25297 options = Roo.apply({}, options, {
25298 strokeColor: "#0000FF",
25299 strokeOpacity: .35,
25301 fillColor: "#0000FF",
25305 options.map = this.gMapContext.map;
25306 options.radius = radius;
25307 options.center = center;
25308 this.gMapContext.circle = new google.maps.Circle(options);
25309 return this.gMapContext.circle;
25315 setPosition: function(location)
25317 this.gMapContext.location = location;
25318 this.gMapContext.marker.setPosition(location);
25319 this.gMapContext.map.panTo(location);
25320 this.drawCircle(location, this.gMapContext.radius, {});
25324 if (this.gMapContext.settings.enableReverseGeocode) {
25325 this.gMapContext.geodecoder.geocode({
25326 latLng: this.gMapContext.location
25327 }, function(results, status) {
25329 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25330 _this.gMapContext.locationName = results[0].formatted_address;
25331 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25333 _this.fireEvent('positionchanged', this, location);
25340 this.fireEvent('positionchanged', this, location);
25345 google.maps.event.trigger(this.gMapContext.map, "resize");
25347 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25349 this.fireEvent('resize', this);
25352 setPositionByLatLng: function(latitude, longitude)
25354 this.setPosition(new google.maps.LatLng(latitude, longitude));
25357 getCurrentPosition: function()
25360 latitude: this.gMapContext.location.lat(),
25361 longitude: this.gMapContext.location.lng()
25365 getAddressName: function()
25367 return this.gMapContext.locationName;
25370 getAddressComponents: function()
25372 return this.gMapContext.addressComponents;
25375 address_component_from_google_geocode: function(address_components)
25379 for (var i = 0; i < address_components.length; i++) {
25380 var component = address_components[i];
25381 if (component.types.indexOf("postal_code") >= 0) {
25382 result.postalCode = component.short_name;
25383 } else if (component.types.indexOf("street_number") >= 0) {
25384 result.streetNumber = component.short_name;
25385 } else if (component.types.indexOf("route") >= 0) {
25386 result.streetName = component.short_name;
25387 } else if (component.types.indexOf("neighborhood") >= 0) {
25388 result.city = component.short_name;
25389 } else if (component.types.indexOf("locality") >= 0) {
25390 result.city = component.short_name;
25391 } else if (component.types.indexOf("sublocality") >= 0) {
25392 result.district = component.short_name;
25393 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25394 result.stateOrProvince = component.short_name;
25395 } else if (component.types.indexOf("country") >= 0) {
25396 result.country = component.short_name;
25400 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25401 result.addressLine2 = "";
25405 setZoomLevel: function(zoom)
25407 this.gMapContext.map.setZoom(zoom);
25420 this.fireEvent('show', this);
25431 this.fireEvent('hide', this);
25436 Roo.apply(Roo.bootstrap.LocationPicker, {
25438 OverlayView : function(map, options)
25440 options = options || {};
25454 * @class Roo.bootstrap.Alert
25455 * @extends Roo.bootstrap.Component
25456 * Bootstrap Alert class
25457 * @cfg {String} title The title of alert
25458 * @cfg {String} html The content of alert
25459 * @cfg {String} weight ( success | info | warning | danger )
25460 * @cfg {String} faicon font-awesomeicon
25463 * Create a new alert
25464 * @param {Object} config The config object
25468 Roo.bootstrap.Alert = function(config){
25469 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25473 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25480 getAutoCreate : function()
25489 cls : 'roo-alert-icon'
25494 cls : 'roo-alert-title',
25499 cls : 'roo-alert-text',
25506 cfg.cn[0].cls += ' fa ' + this.faicon;
25510 cfg.cls += ' alert-' + this.weight;
25516 initEvents: function()
25518 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25521 setTitle : function(str)
25523 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25526 setText : function(str)
25528 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25531 setWeight : function(weight)
25534 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25537 this.weight = weight;
25539 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25542 setIcon : function(icon)
25545 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25548 this.faicon = icon;
25550 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25571 * @class Roo.bootstrap.UploadCropbox
25572 * @extends Roo.bootstrap.Component
25573 * Bootstrap UploadCropbox class
25574 * @cfg {String} emptyText show when image has been loaded
25575 * @cfg {String} rotateNotify show when image too small to rotate
25576 * @cfg {Number} errorTimeout default 3000
25577 * @cfg {Number} minWidth default 300
25578 * @cfg {Number} minHeight default 300
25579 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25580 * @cfg {Boolean} isDocument (true|false) default false
25581 * @cfg {String} url action url
25582 * @cfg {String} paramName default 'imageUpload'
25583 * @cfg {String} method default POST
25584 * @cfg {Boolean} loadMask (true|false) default true
25585 * @cfg {Boolean} loadingText default 'Loading...'
25588 * Create a new UploadCropbox
25589 * @param {Object} config The config object
25592 Roo.bootstrap.UploadCropbox = function(config){
25593 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25597 * @event beforeselectfile
25598 * Fire before select file
25599 * @param {Roo.bootstrap.UploadCropbox} this
25601 "beforeselectfile" : true,
25604 * Fire after initEvent
25605 * @param {Roo.bootstrap.UploadCropbox} this
25610 * Fire after initEvent
25611 * @param {Roo.bootstrap.UploadCropbox} this
25612 * @param {String} data
25617 * Fire when preparing the file data
25618 * @param {Roo.bootstrap.UploadCropbox} this
25619 * @param {Object} file
25624 * Fire when get exception
25625 * @param {Roo.bootstrap.UploadCropbox} this
25626 * @param {XMLHttpRequest} xhr
25628 "exception" : true,
25630 * @event beforeloadcanvas
25631 * Fire before load the canvas
25632 * @param {Roo.bootstrap.UploadCropbox} this
25633 * @param {String} src
25635 "beforeloadcanvas" : true,
25638 * Fire when trash image
25639 * @param {Roo.bootstrap.UploadCropbox} this
25644 * Fire when download the image
25645 * @param {Roo.bootstrap.UploadCropbox} this
25649 * @event footerbuttonclick
25650 * Fire when footerbuttonclick
25651 * @param {Roo.bootstrap.UploadCropbox} this
25652 * @param {String} type
25654 "footerbuttonclick" : true,
25658 * @param {Roo.bootstrap.UploadCropbox} this
25663 * Fire when rotate the image
25664 * @param {Roo.bootstrap.UploadCropbox} this
25665 * @param {String} pos
25670 * Fire when inspect the file
25671 * @param {Roo.bootstrap.UploadCropbox} this
25672 * @param {Object} file
25677 * Fire when xhr upload the file
25678 * @param {Roo.bootstrap.UploadCropbox} this
25679 * @param {Object} data
25684 * Fire when arrange the file data
25685 * @param {Roo.bootstrap.UploadCropbox} this
25686 * @param {Object} formData
25691 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25694 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25696 emptyText : 'Click to upload image',
25697 rotateNotify : 'Image is too small to rotate',
25698 errorTimeout : 3000,
25712 cropType : 'image/jpeg',
25714 canvasLoaded : false,
25715 isDocument : false,
25717 paramName : 'imageUpload',
25719 loadingText : 'Loading...',
25722 getAutoCreate : function()
25726 cls : 'roo-upload-cropbox',
25730 cls : 'roo-upload-cropbox-selector',
25735 cls : 'roo-upload-cropbox-body',
25736 style : 'cursor:pointer',
25740 cls : 'roo-upload-cropbox-preview'
25744 cls : 'roo-upload-cropbox-thumb'
25748 cls : 'roo-upload-cropbox-empty-notify',
25749 html : this.emptyText
25753 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25754 html : this.rotateNotify
25760 cls : 'roo-upload-cropbox-footer',
25763 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25773 onRender : function(ct, position)
25775 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25777 if (this.buttons.length) {
25779 Roo.each(this.buttons, function(bb) {
25781 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25783 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25789 this.maskEl = this.el;
25793 initEvents : function()
25795 this.urlAPI = (window.createObjectURL && window) ||
25796 (window.URL && URL.revokeObjectURL && URL) ||
25797 (window.webkitURL && webkitURL);
25799 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25800 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25802 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25803 this.selectorEl.hide();
25805 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25806 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25808 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25809 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25810 this.thumbEl.hide();
25812 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25813 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25815 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25816 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25817 this.errorEl.hide();
25819 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25820 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25821 this.footerEl.hide();
25823 this.setThumbBoxSize();
25829 this.fireEvent('initial', this);
25836 window.addEventListener("resize", function() { _this.resize(); } );
25838 this.bodyEl.on('click', this.beforeSelectFile, this);
25841 this.bodyEl.on('touchstart', this.onTouchStart, this);
25842 this.bodyEl.on('touchmove', this.onTouchMove, this);
25843 this.bodyEl.on('touchend', this.onTouchEnd, this);
25847 this.bodyEl.on('mousedown', this.onMouseDown, this);
25848 this.bodyEl.on('mousemove', this.onMouseMove, this);
25849 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25850 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25851 Roo.get(document).on('mouseup', this.onMouseUp, this);
25854 this.selectorEl.on('change', this.onFileSelected, this);
25860 this.baseScale = 1;
25862 this.baseRotate = 1;
25863 this.dragable = false;
25864 this.pinching = false;
25867 this.cropData = false;
25868 this.notifyEl.dom.innerHTML = this.emptyText;
25870 this.selectorEl.dom.value = '';
25874 resize : function()
25876 if(this.fireEvent('resize', this) != false){
25877 this.setThumbBoxPosition();
25878 this.setCanvasPosition();
25882 onFooterButtonClick : function(e, el, o, type)
25885 case 'rotate-left' :
25886 this.onRotateLeft(e);
25888 case 'rotate-right' :
25889 this.onRotateRight(e);
25892 this.beforeSelectFile(e);
25907 this.fireEvent('footerbuttonclick', this, type);
25910 beforeSelectFile : function(e)
25912 e.preventDefault();
25914 if(this.fireEvent('beforeselectfile', this) != false){
25915 this.selectorEl.dom.click();
25919 onFileSelected : function(e)
25921 e.preventDefault();
25923 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25927 var file = this.selectorEl.dom.files[0];
25929 if(this.fireEvent('inspect', this, file) != false){
25930 this.prepare(file);
25935 trash : function(e)
25937 this.fireEvent('trash', this);
25940 download : function(e)
25942 this.fireEvent('download', this);
25945 loadCanvas : function(src)
25947 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25951 this.imageEl = document.createElement('img');
25955 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25957 this.imageEl.src = src;
25961 onLoadCanvas : function()
25963 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25964 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25966 this.bodyEl.un('click', this.beforeSelectFile, this);
25968 this.notifyEl.hide();
25969 this.thumbEl.show();
25970 this.footerEl.show();
25972 this.baseRotateLevel();
25974 if(this.isDocument){
25975 this.setThumbBoxSize();
25978 this.setThumbBoxPosition();
25980 this.baseScaleLevel();
25986 this.canvasLoaded = true;
25989 this.maskEl.unmask();
25994 setCanvasPosition : function()
25996 if(!this.canvasEl){
26000 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26001 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26003 this.previewEl.setLeft(pw);
26004 this.previewEl.setTop(ph);
26008 onMouseDown : function(e)
26012 this.dragable = true;
26013 this.pinching = false;
26015 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26016 this.dragable = false;
26020 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26021 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26025 onMouseMove : function(e)
26029 if(!this.canvasLoaded){
26033 if (!this.dragable){
26037 var minX = Math.ceil(this.thumbEl.getLeft(true));
26038 var minY = Math.ceil(this.thumbEl.getTop(true));
26040 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26041 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26043 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26044 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26046 x = x - this.mouseX;
26047 y = y - this.mouseY;
26049 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26050 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26052 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26053 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26055 this.previewEl.setLeft(bgX);
26056 this.previewEl.setTop(bgY);
26058 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26059 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26062 onMouseUp : function(e)
26066 this.dragable = false;
26069 onMouseWheel : function(e)
26073 this.startScale = this.scale;
26075 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26077 if(!this.zoomable()){
26078 this.scale = this.startScale;
26087 zoomable : function()
26089 var minScale = this.thumbEl.getWidth() / this.minWidth;
26091 if(this.minWidth < this.minHeight){
26092 minScale = this.thumbEl.getHeight() / this.minHeight;
26095 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26096 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26100 (this.rotate == 0 || this.rotate == 180) &&
26102 width > this.imageEl.OriginWidth ||
26103 height > this.imageEl.OriginHeight ||
26104 (width < this.minWidth && height < this.minHeight)
26112 (this.rotate == 90 || this.rotate == 270) &&
26114 width > this.imageEl.OriginWidth ||
26115 height > this.imageEl.OriginHeight ||
26116 (width < this.minHeight && height < this.minWidth)
26123 !this.isDocument &&
26124 (this.rotate == 0 || this.rotate == 180) &&
26126 width < this.minWidth ||
26127 width > this.imageEl.OriginWidth ||
26128 height < this.minHeight ||
26129 height > this.imageEl.OriginHeight
26136 !this.isDocument &&
26137 (this.rotate == 90 || this.rotate == 270) &&
26139 width < this.minHeight ||
26140 width > this.imageEl.OriginWidth ||
26141 height < this.minWidth ||
26142 height > this.imageEl.OriginHeight
26152 onRotateLeft : function(e)
26154 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26156 var minScale = this.thumbEl.getWidth() / this.minWidth;
26158 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26159 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26161 this.startScale = this.scale;
26163 while (this.getScaleLevel() < minScale){
26165 this.scale = this.scale + 1;
26167 if(!this.zoomable()){
26172 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26173 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26178 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26185 this.scale = this.startScale;
26187 this.onRotateFail();
26192 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26194 if(this.isDocument){
26195 this.setThumbBoxSize();
26196 this.setThumbBoxPosition();
26197 this.setCanvasPosition();
26202 this.fireEvent('rotate', this, 'left');
26206 onRotateRight : function(e)
26208 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26210 var minScale = this.thumbEl.getWidth() / this.minWidth;
26212 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26213 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26215 this.startScale = this.scale;
26217 while (this.getScaleLevel() < minScale){
26219 this.scale = this.scale + 1;
26221 if(!this.zoomable()){
26226 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26227 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26232 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26239 this.scale = this.startScale;
26241 this.onRotateFail();
26246 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26248 if(this.isDocument){
26249 this.setThumbBoxSize();
26250 this.setThumbBoxPosition();
26251 this.setCanvasPosition();
26256 this.fireEvent('rotate', this, 'right');
26259 onRotateFail : function()
26261 this.errorEl.show(true);
26265 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26270 this.previewEl.dom.innerHTML = '';
26272 var canvasEl = document.createElement("canvas");
26274 var contextEl = canvasEl.getContext("2d");
26276 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26277 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26278 var center = this.imageEl.OriginWidth / 2;
26280 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26281 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26282 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26283 center = this.imageEl.OriginHeight / 2;
26286 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26288 contextEl.translate(center, center);
26289 contextEl.rotate(this.rotate * Math.PI / 180);
26291 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26293 this.canvasEl = document.createElement("canvas");
26295 this.contextEl = this.canvasEl.getContext("2d");
26297 switch (this.rotate) {
26300 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26301 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26303 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26308 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26309 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26311 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26312 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);
26316 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26321 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26322 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26324 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26325 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);
26329 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);
26334 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26335 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26337 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26338 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26342 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);
26349 this.previewEl.appendChild(this.canvasEl);
26351 this.setCanvasPosition();
26356 if(!this.canvasLoaded){
26360 var imageCanvas = document.createElement("canvas");
26362 var imageContext = imageCanvas.getContext("2d");
26364 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26365 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26367 var center = imageCanvas.width / 2;
26369 imageContext.translate(center, center);
26371 imageContext.rotate(this.rotate * Math.PI / 180);
26373 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26375 var canvas = document.createElement("canvas");
26377 var context = canvas.getContext("2d");
26379 canvas.width = this.minWidth;
26380 canvas.height = this.minHeight;
26382 switch (this.rotate) {
26385 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26386 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26388 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26389 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26391 var targetWidth = this.minWidth - 2 * x;
26392 var targetHeight = this.minHeight - 2 * y;
26396 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26397 scale = targetWidth / width;
26400 if(x > 0 && y == 0){
26401 scale = targetHeight / height;
26404 if(x > 0 && y > 0){
26405 scale = targetWidth / width;
26407 if(width < height){
26408 scale = targetHeight / height;
26412 context.scale(scale, scale);
26414 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26415 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26417 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26418 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26420 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26425 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26426 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26428 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26429 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26431 var targetWidth = this.minWidth - 2 * x;
26432 var targetHeight = this.minHeight - 2 * y;
26436 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26437 scale = targetWidth / width;
26440 if(x > 0 && y == 0){
26441 scale = targetHeight / height;
26444 if(x > 0 && y > 0){
26445 scale = targetWidth / width;
26447 if(width < height){
26448 scale = targetHeight / height;
26452 context.scale(scale, scale);
26454 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26455 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26457 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26458 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26460 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26462 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26467 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26468 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26470 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26471 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26473 var targetWidth = this.minWidth - 2 * x;
26474 var targetHeight = this.minHeight - 2 * y;
26478 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26479 scale = targetWidth / width;
26482 if(x > 0 && y == 0){
26483 scale = targetHeight / height;
26486 if(x > 0 && y > 0){
26487 scale = targetWidth / width;
26489 if(width < height){
26490 scale = targetHeight / height;
26494 context.scale(scale, scale);
26496 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26497 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26499 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26500 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26502 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26503 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26505 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26510 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26511 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26513 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26514 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26516 var targetWidth = this.minWidth - 2 * x;
26517 var targetHeight = this.minHeight - 2 * y;
26521 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26522 scale = targetWidth / width;
26525 if(x > 0 && y == 0){
26526 scale = targetHeight / height;
26529 if(x > 0 && y > 0){
26530 scale = targetWidth / width;
26532 if(width < height){
26533 scale = targetHeight / height;
26537 context.scale(scale, scale);
26539 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26540 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26542 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26543 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26545 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26547 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26554 this.cropData = canvas.toDataURL(this.cropType);
26556 if(this.fireEvent('crop', this, this.cropData) !== false){
26557 this.process(this.file, this.cropData);
26564 setThumbBoxSize : function()
26568 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26569 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26570 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26572 this.minWidth = width;
26573 this.minHeight = height;
26575 if(this.rotate == 90 || this.rotate == 270){
26576 this.minWidth = height;
26577 this.minHeight = width;
26582 width = Math.ceil(this.minWidth * height / this.minHeight);
26584 if(this.minWidth > this.minHeight){
26586 height = Math.ceil(this.minHeight * width / this.minWidth);
26589 this.thumbEl.setStyle({
26590 width : width + 'px',
26591 height : height + 'px'
26598 setThumbBoxPosition : function()
26600 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26601 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26603 this.thumbEl.setLeft(x);
26604 this.thumbEl.setTop(y);
26608 baseRotateLevel : function()
26610 this.baseRotate = 1;
26613 typeof(this.exif) != 'undefined' &&
26614 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26615 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26617 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26620 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26624 baseScaleLevel : function()
26628 if(this.isDocument){
26630 if(this.baseRotate == 6 || this.baseRotate == 8){
26632 height = this.thumbEl.getHeight();
26633 this.baseScale = height / this.imageEl.OriginWidth;
26635 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26636 width = this.thumbEl.getWidth();
26637 this.baseScale = width / this.imageEl.OriginHeight;
26643 height = this.thumbEl.getHeight();
26644 this.baseScale = height / this.imageEl.OriginHeight;
26646 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26647 width = this.thumbEl.getWidth();
26648 this.baseScale = width / this.imageEl.OriginWidth;
26654 if(this.baseRotate == 6 || this.baseRotate == 8){
26656 width = this.thumbEl.getHeight();
26657 this.baseScale = width / this.imageEl.OriginHeight;
26659 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26660 height = this.thumbEl.getWidth();
26661 this.baseScale = height / this.imageEl.OriginHeight;
26664 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26665 height = this.thumbEl.getWidth();
26666 this.baseScale = height / this.imageEl.OriginHeight;
26668 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26669 width = this.thumbEl.getHeight();
26670 this.baseScale = width / this.imageEl.OriginWidth;
26677 width = this.thumbEl.getWidth();
26678 this.baseScale = width / this.imageEl.OriginWidth;
26680 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26681 height = this.thumbEl.getHeight();
26682 this.baseScale = height / this.imageEl.OriginHeight;
26685 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26687 height = this.thumbEl.getHeight();
26688 this.baseScale = height / this.imageEl.OriginHeight;
26690 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26691 width = this.thumbEl.getWidth();
26692 this.baseScale = width / this.imageEl.OriginWidth;
26700 getScaleLevel : function()
26702 return this.baseScale * Math.pow(1.1, this.scale);
26705 onTouchStart : function(e)
26707 if(!this.canvasLoaded){
26708 this.beforeSelectFile(e);
26712 var touches = e.browserEvent.touches;
26718 if(touches.length == 1){
26719 this.onMouseDown(e);
26723 if(touches.length != 2){
26729 for(var i = 0, finger; finger = touches[i]; i++){
26730 coords.push(finger.pageX, finger.pageY);
26733 var x = Math.pow(coords[0] - coords[2], 2);
26734 var y = Math.pow(coords[1] - coords[3], 2);
26736 this.startDistance = Math.sqrt(x + y);
26738 this.startScale = this.scale;
26740 this.pinching = true;
26741 this.dragable = false;
26745 onTouchMove : function(e)
26747 if(!this.pinching && !this.dragable){
26751 var touches = e.browserEvent.touches;
26758 this.onMouseMove(e);
26764 for(var i = 0, finger; finger = touches[i]; i++){
26765 coords.push(finger.pageX, finger.pageY);
26768 var x = Math.pow(coords[0] - coords[2], 2);
26769 var y = Math.pow(coords[1] - coords[3], 2);
26771 this.endDistance = Math.sqrt(x + y);
26773 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26775 if(!this.zoomable()){
26776 this.scale = this.startScale;
26784 onTouchEnd : function(e)
26786 this.pinching = false;
26787 this.dragable = false;
26791 process : function(file, crop)
26794 this.maskEl.mask(this.loadingText);
26797 this.xhr = new XMLHttpRequest();
26799 file.xhr = this.xhr;
26801 this.xhr.open(this.method, this.url, true);
26804 "Accept": "application/json",
26805 "Cache-Control": "no-cache",
26806 "X-Requested-With": "XMLHttpRequest"
26809 for (var headerName in headers) {
26810 var headerValue = headers[headerName];
26812 this.xhr.setRequestHeader(headerName, headerValue);
26818 this.xhr.onload = function()
26820 _this.xhrOnLoad(_this.xhr);
26823 this.xhr.onerror = function()
26825 _this.xhrOnError(_this.xhr);
26828 var formData = new FormData();
26830 formData.append('returnHTML', 'NO');
26833 formData.append('crop', crop);
26836 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26837 formData.append(this.paramName, file, file.name);
26840 if(typeof(file.filename) != 'undefined'){
26841 formData.append('filename', file.filename);
26844 if(typeof(file.mimetype) != 'undefined'){
26845 formData.append('mimetype', file.mimetype);
26848 if(this.fireEvent('arrange', this, formData) != false){
26849 this.xhr.send(formData);
26853 xhrOnLoad : function(xhr)
26856 this.maskEl.unmask();
26859 if (xhr.readyState !== 4) {
26860 this.fireEvent('exception', this, xhr);
26864 var response = Roo.decode(xhr.responseText);
26866 if(!response.success){
26867 this.fireEvent('exception', this, xhr);
26871 var response = Roo.decode(xhr.responseText);
26873 this.fireEvent('upload', this, response);
26877 xhrOnError : function()
26880 this.maskEl.unmask();
26883 Roo.log('xhr on error');
26885 var response = Roo.decode(xhr.responseText);
26891 prepare : function(file)
26894 this.maskEl.mask(this.loadingText);
26900 if(typeof(file) === 'string'){
26901 this.loadCanvas(file);
26905 if(!file || !this.urlAPI){
26910 this.cropType = file.type;
26914 if(this.fireEvent('prepare', this, this.file) != false){
26916 var reader = new FileReader();
26918 reader.onload = function (e) {
26919 if (e.target.error) {
26920 Roo.log(e.target.error);
26924 var buffer = e.target.result,
26925 dataView = new DataView(buffer),
26927 maxOffset = dataView.byteLength - 4,
26931 if (dataView.getUint16(0) === 0xffd8) {
26932 while (offset < maxOffset) {
26933 markerBytes = dataView.getUint16(offset);
26935 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26936 markerLength = dataView.getUint16(offset + 2) + 2;
26937 if (offset + markerLength > dataView.byteLength) {
26938 Roo.log('Invalid meta data: Invalid segment size.');
26942 if(markerBytes == 0xffe1){
26943 _this.parseExifData(
26950 offset += markerLength;
26960 var url = _this.urlAPI.createObjectURL(_this.file);
26962 _this.loadCanvas(url);
26967 reader.readAsArrayBuffer(this.file);
26973 parseExifData : function(dataView, offset, length)
26975 var tiffOffset = offset + 10,
26979 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26980 // No Exif data, might be XMP data instead
26984 // Check for the ASCII code for "Exif" (0x45786966):
26985 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26986 // No Exif data, might be XMP data instead
26989 if (tiffOffset + 8 > dataView.byteLength) {
26990 Roo.log('Invalid Exif data: Invalid segment size.');
26993 // Check for the two null bytes:
26994 if (dataView.getUint16(offset + 8) !== 0x0000) {
26995 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26998 // Check the byte alignment:
26999 switch (dataView.getUint16(tiffOffset)) {
27001 littleEndian = true;
27004 littleEndian = false;
27007 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27010 // Check for the TIFF tag marker (0x002A):
27011 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27012 Roo.log('Invalid Exif data: Missing TIFF marker.');
27015 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27016 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27018 this.parseExifTags(
27021 tiffOffset + dirOffset,
27026 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27031 if (dirOffset + 6 > dataView.byteLength) {
27032 Roo.log('Invalid Exif data: Invalid directory offset.');
27035 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27036 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27037 if (dirEndOffset + 4 > dataView.byteLength) {
27038 Roo.log('Invalid Exif data: Invalid directory size.');
27041 for (i = 0; i < tagsNumber; i += 1) {
27045 dirOffset + 2 + 12 * i, // tag offset
27049 // Return the offset to the next directory:
27050 return dataView.getUint32(dirEndOffset, littleEndian);
27053 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27055 var tag = dataView.getUint16(offset, littleEndian);
27057 this.exif[tag] = this.getExifValue(
27061 dataView.getUint16(offset + 2, littleEndian), // tag type
27062 dataView.getUint32(offset + 4, littleEndian), // tag length
27067 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27069 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27078 Roo.log('Invalid Exif data: Invalid tag type.');
27082 tagSize = tagType.size * length;
27083 // Determine if the value is contained in the dataOffset bytes,
27084 // or if the value at the dataOffset is a pointer to the actual data:
27085 dataOffset = tagSize > 4 ?
27086 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27087 if (dataOffset + tagSize > dataView.byteLength) {
27088 Roo.log('Invalid Exif data: Invalid data offset.');
27091 if (length === 1) {
27092 return tagType.getValue(dataView, dataOffset, littleEndian);
27095 for (i = 0; i < length; i += 1) {
27096 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27099 if (tagType.ascii) {
27101 // Concatenate the chars:
27102 for (i = 0; i < values.length; i += 1) {
27104 // Ignore the terminating NULL byte(s):
27105 if (c === '\u0000') {
27117 Roo.apply(Roo.bootstrap.UploadCropbox, {
27119 'Orientation': 0x0112
27123 1: 0, //'top-left',
27125 3: 180, //'bottom-right',
27126 // 4: 'bottom-left',
27128 6: 90, //'right-top',
27129 // 7: 'right-bottom',
27130 8: 270 //'left-bottom'
27134 // byte, 8-bit unsigned int:
27136 getValue: function (dataView, dataOffset) {
27137 return dataView.getUint8(dataOffset);
27141 // ascii, 8-bit byte:
27143 getValue: function (dataView, dataOffset) {
27144 return String.fromCharCode(dataView.getUint8(dataOffset));
27149 // short, 16 bit int:
27151 getValue: function (dataView, dataOffset, littleEndian) {
27152 return dataView.getUint16(dataOffset, littleEndian);
27156 // long, 32 bit int:
27158 getValue: function (dataView, dataOffset, littleEndian) {
27159 return dataView.getUint32(dataOffset, littleEndian);
27163 // rational = two long values, first is numerator, second is denominator:
27165 getValue: function (dataView, dataOffset, littleEndian) {
27166 return dataView.getUint32(dataOffset, littleEndian) /
27167 dataView.getUint32(dataOffset + 4, littleEndian);
27171 // slong, 32 bit signed int:
27173 getValue: function (dataView, dataOffset, littleEndian) {
27174 return dataView.getInt32(dataOffset, littleEndian);
27178 // srational, two slongs, first is numerator, second is denominator:
27180 getValue: function (dataView, dataOffset, littleEndian) {
27181 return dataView.getInt32(dataOffset, littleEndian) /
27182 dataView.getInt32(dataOffset + 4, littleEndian);
27192 cls : 'btn-group roo-upload-cropbox-rotate-left',
27193 action : 'rotate-left',
27197 cls : 'btn btn-default',
27198 html : '<i class="fa fa-undo"></i>'
27204 cls : 'btn-group roo-upload-cropbox-picture',
27205 action : 'picture',
27209 cls : 'btn btn-default',
27210 html : '<i class="fa fa-picture-o"></i>'
27216 cls : 'btn-group roo-upload-cropbox-rotate-right',
27217 action : 'rotate-right',
27221 cls : 'btn btn-default',
27222 html : '<i class="fa fa-repeat"></i>'
27230 cls : 'btn-group roo-upload-cropbox-rotate-left',
27231 action : 'rotate-left',
27235 cls : 'btn btn-default',
27236 html : '<i class="fa fa-undo"></i>'
27242 cls : 'btn-group roo-upload-cropbox-download',
27243 action : 'download',
27247 cls : 'btn btn-default',
27248 html : '<i class="fa fa-download"></i>'
27254 cls : 'btn-group roo-upload-cropbox-crop',
27259 cls : 'btn btn-default',
27260 html : '<i class="fa fa-crop"></i>'
27266 cls : 'btn-group roo-upload-cropbox-trash',
27271 cls : 'btn btn-default',
27272 html : '<i class="fa fa-trash"></i>'
27278 cls : 'btn-group roo-upload-cropbox-rotate-right',
27279 action : 'rotate-right',
27283 cls : 'btn btn-default',
27284 html : '<i class="fa fa-repeat"></i>'
27292 cls : 'btn-group roo-upload-cropbox-rotate-left',
27293 action : 'rotate-left',
27297 cls : 'btn btn-default',
27298 html : '<i class="fa fa-undo"></i>'
27304 cls : 'btn-group roo-upload-cropbox-rotate-right',
27305 action : 'rotate-right',
27309 cls : 'btn btn-default',
27310 html : '<i class="fa fa-repeat"></i>'
27323 * @class Roo.bootstrap.DocumentManager
27324 * @extends Roo.bootstrap.Component
27325 * Bootstrap DocumentManager class
27326 * @cfg {String} paramName default 'imageUpload'
27327 * @cfg {String} toolTipName default 'filename'
27328 * @cfg {String} method default POST
27329 * @cfg {String} url action url
27330 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27331 * @cfg {Boolean} multiple multiple upload default true
27332 * @cfg {Number} thumbSize default 300
27333 * @cfg {String} fieldLabel
27334 * @cfg {Number} labelWidth default 4
27335 * @cfg {String} labelAlign (left|top) default left
27336 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27339 * Create a new DocumentManager
27340 * @param {Object} config The config object
27343 Roo.bootstrap.DocumentManager = function(config){
27344 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27347 this.delegates = [];
27352 * Fire when initial the DocumentManager
27353 * @param {Roo.bootstrap.DocumentManager} this
27358 * inspect selected file
27359 * @param {Roo.bootstrap.DocumentManager} this
27360 * @param {File} file
27365 * Fire when xhr load exception
27366 * @param {Roo.bootstrap.DocumentManager} this
27367 * @param {XMLHttpRequest} xhr
27369 "exception" : true,
27371 * @event afterupload
27372 * Fire when xhr load exception
27373 * @param {Roo.bootstrap.DocumentManager} this
27374 * @param {XMLHttpRequest} xhr
27376 "afterupload" : true,
27379 * prepare the form data
27380 * @param {Roo.bootstrap.DocumentManager} this
27381 * @param {Object} formData
27386 * Fire when remove the file
27387 * @param {Roo.bootstrap.DocumentManager} this
27388 * @param {Object} file
27393 * Fire after refresh the file
27394 * @param {Roo.bootstrap.DocumentManager} this
27399 * Fire after click the image
27400 * @param {Roo.bootstrap.DocumentManager} this
27401 * @param {Object} file
27406 * Fire when upload a image and editable set to true
27407 * @param {Roo.bootstrap.DocumentManager} this
27408 * @param {Object} file
27412 * @event beforeselectfile
27413 * Fire before select file
27414 * @param {Roo.bootstrap.DocumentManager} this
27416 "beforeselectfile" : true,
27419 * Fire before process file
27420 * @param {Roo.bootstrap.DocumentManager} this
27421 * @param {Object} file
27428 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27437 paramName : 'imageUpload',
27438 toolTipName : 'filename',
27441 labelAlign : 'left',
27446 getAutoCreate : function()
27448 var managerWidget = {
27450 cls : 'roo-document-manager',
27454 cls : 'roo-document-manager-selector',
27459 cls : 'roo-document-manager-uploader',
27463 cls : 'roo-document-manager-upload-btn',
27464 html : '<i class="fa fa-plus"></i>'
27475 cls : 'column col-md-12',
27480 if(this.fieldLabel.length){
27485 cls : 'column col-md-12',
27486 html : this.fieldLabel
27490 cls : 'column col-md-12',
27495 if(this.labelAlign == 'left'){
27499 cls : 'column col-md-' + this.labelWidth,
27500 html : this.fieldLabel
27504 cls : 'column col-md-' + (12 - this.labelWidth),
27514 cls : 'row clearfix',
27522 initEvents : function()
27524 this.managerEl = this.el.select('.roo-document-manager', true).first();
27525 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27527 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27528 this.selectorEl.hide();
27531 this.selectorEl.attr('multiple', 'multiple');
27534 this.selectorEl.on('change', this.onFileSelected, this);
27536 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27537 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27539 this.uploader.on('click', this.onUploaderClick, this);
27541 this.renderProgressDialog();
27545 window.addEventListener("resize", function() { _this.refresh(); } );
27547 this.fireEvent('initial', this);
27550 renderProgressDialog : function()
27554 this.progressDialog = new Roo.bootstrap.Modal({
27555 cls : 'roo-document-manager-progress-dialog',
27556 allow_close : false,
27566 btnclick : function() {
27567 _this.uploadCancel();
27573 this.progressDialog.render(Roo.get(document.body));
27575 this.progress = new Roo.bootstrap.Progress({
27576 cls : 'roo-document-manager-progress',
27581 this.progress.render(this.progressDialog.getChildContainer());
27583 this.progressBar = new Roo.bootstrap.ProgressBar({
27584 cls : 'roo-document-manager-progress-bar',
27587 aria_valuemax : 12,
27591 this.progressBar.render(this.progress.getChildContainer());
27594 onUploaderClick : function(e)
27596 e.preventDefault();
27598 if(this.fireEvent('beforeselectfile', this) != false){
27599 this.selectorEl.dom.click();
27604 onFileSelected : function(e)
27606 e.preventDefault();
27608 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27612 Roo.each(this.selectorEl.dom.files, function(file){
27613 if(this.fireEvent('inspect', this, file) != false){
27614 this.files.push(file);
27624 this.selectorEl.dom.value = '';
27626 if(!this.files.length){
27630 if(this.boxes > 0 && this.files.length > this.boxes){
27631 this.files = this.files.slice(0, this.boxes);
27634 this.uploader.show();
27636 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27637 this.uploader.hide();
27646 Roo.each(this.files, function(file){
27648 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27649 var f = this.renderPreview(file);
27654 if(file.type.indexOf('image') != -1){
27655 this.delegates.push(
27657 _this.process(file);
27658 }).createDelegate(this)
27666 _this.process(file);
27667 }).createDelegate(this)
27672 this.files = files;
27674 this.delegates = this.delegates.concat(docs);
27676 if(!this.delegates.length){
27681 this.progressBar.aria_valuemax = this.delegates.length;
27688 arrange : function()
27690 if(!this.delegates.length){
27691 this.progressDialog.hide();
27696 var delegate = this.delegates.shift();
27698 this.progressDialog.show();
27700 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27702 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27707 refresh : function()
27709 this.uploader.show();
27711 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27712 this.uploader.hide();
27715 Roo.isTouch ? this.closable(false) : this.closable(true);
27717 this.fireEvent('refresh', this);
27720 onRemove : function(e, el, o)
27722 e.preventDefault();
27724 this.fireEvent('remove', this, o);
27728 remove : function(o)
27732 Roo.each(this.files, function(file){
27733 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27742 this.files = files;
27749 Roo.each(this.files, function(file){
27754 file.target.remove();
27763 onClick : function(e, el, o)
27765 e.preventDefault();
27767 this.fireEvent('click', this, o);
27771 closable : function(closable)
27773 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27775 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27787 xhrOnLoad : function(xhr)
27789 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27793 if (xhr.readyState !== 4) {
27795 this.fireEvent('exception', this, xhr);
27799 var response = Roo.decode(xhr.responseText);
27801 if(!response.success){
27803 this.fireEvent('exception', this, xhr);
27807 var file = this.renderPreview(response.data);
27809 this.files.push(file);
27813 this.fireEvent('afterupload', this, xhr);
27817 xhrOnError : function(xhr)
27819 Roo.log('xhr on error');
27821 var response = Roo.decode(xhr.responseText);
27828 process : function(file)
27830 if(this.fireEvent('process', this, file) !== false){
27831 if(this.editable && file.type.indexOf('image') != -1){
27832 this.fireEvent('edit', this, file);
27836 this.uploadStart(file, false);
27843 uploadStart : function(file, crop)
27845 this.xhr = new XMLHttpRequest();
27847 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27852 file.xhr = this.xhr;
27854 this.managerEl.createChild({
27856 cls : 'roo-document-manager-loading',
27860 tooltip : file.name,
27861 cls : 'roo-document-manager-thumb',
27862 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27868 this.xhr.open(this.method, this.url, true);
27871 "Accept": "application/json",
27872 "Cache-Control": "no-cache",
27873 "X-Requested-With": "XMLHttpRequest"
27876 for (var headerName in headers) {
27877 var headerValue = headers[headerName];
27879 this.xhr.setRequestHeader(headerName, headerValue);
27885 this.xhr.onload = function()
27887 _this.xhrOnLoad(_this.xhr);
27890 this.xhr.onerror = function()
27892 _this.xhrOnError(_this.xhr);
27895 var formData = new FormData();
27897 formData.append('returnHTML', 'NO');
27900 formData.append('crop', crop);
27903 formData.append(this.paramName, file, file.name);
27910 if(this.fireEvent('prepare', this, formData, options) != false){
27912 if(options.manually){
27916 this.xhr.send(formData);
27920 this.uploadCancel();
27923 uploadCancel : function()
27929 this.delegates = [];
27931 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27938 renderPreview : function(file)
27940 if(typeof(file.target) != 'undefined' && file.target){
27944 var previewEl = this.managerEl.createChild({
27946 cls : 'roo-document-manager-preview',
27950 tooltip : file[this.toolTipName],
27951 cls : 'roo-document-manager-thumb',
27952 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27957 html : '<i class="fa fa-times-circle"></i>'
27962 var close = previewEl.select('button.close', true).first();
27964 close.on('click', this.onRemove, this, file);
27966 file.target = previewEl;
27968 var image = previewEl.select('img', true).first();
27972 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27974 image.on('click', this.onClick, this, file);
27980 onPreviewLoad : function(file, image)
27982 if(typeof(file.target) == 'undefined' || !file.target){
27986 var width = image.dom.naturalWidth || image.dom.width;
27987 var height = image.dom.naturalHeight || image.dom.height;
27989 if(width > height){
27990 file.target.addClass('wide');
27994 file.target.addClass('tall');
27999 uploadFromSource : function(file, crop)
28001 this.xhr = new XMLHttpRequest();
28003 this.managerEl.createChild({
28005 cls : 'roo-document-manager-loading',
28009 tooltip : file.name,
28010 cls : 'roo-document-manager-thumb',
28011 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28017 this.xhr.open(this.method, this.url, true);
28020 "Accept": "application/json",
28021 "Cache-Control": "no-cache",
28022 "X-Requested-With": "XMLHttpRequest"
28025 for (var headerName in headers) {
28026 var headerValue = headers[headerName];
28028 this.xhr.setRequestHeader(headerName, headerValue);
28034 this.xhr.onload = function()
28036 _this.xhrOnLoad(_this.xhr);
28039 this.xhr.onerror = function()
28041 _this.xhrOnError(_this.xhr);
28044 var formData = new FormData();
28046 formData.append('returnHTML', 'NO');
28048 formData.append('crop', crop);
28050 if(typeof(file.filename) != 'undefined'){
28051 formData.append('filename', file.filename);
28054 if(typeof(file.mimetype) != 'undefined'){
28055 formData.append('mimetype', file.mimetype);
28058 if(this.fireEvent('prepare', this, formData) != false){
28059 this.xhr.send(formData);
28069 * @class Roo.bootstrap.DocumentViewer
28070 * @extends Roo.bootstrap.Component
28071 * Bootstrap DocumentViewer class
28072 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28073 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28076 * Create a new DocumentViewer
28077 * @param {Object} config The config object
28080 Roo.bootstrap.DocumentViewer = function(config){
28081 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28086 * Fire after initEvent
28087 * @param {Roo.bootstrap.DocumentViewer} this
28093 * @param {Roo.bootstrap.DocumentViewer} this
28098 * Fire after download button
28099 * @param {Roo.bootstrap.DocumentViewer} this
28104 * Fire after trash button
28105 * @param {Roo.bootstrap.DocumentViewer} this
28112 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28114 showDownload : true,
28118 getAutoCreate : function()
28122 cls : 'roo-document-viewer',
28126 cls : 'roo-document-viewer-body',
28130 cls : 'roo-document-viewer-thumb',
28134 cls : 'roo-document-viewer-image'
28142 cls : 'roo-document-viewer-footer',
28145 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28149 cls : 'btn-group roo-document-viewer-download',
28153 cls : 'btn btn-default',
28154 html : '<i class="fa fa-download"></i>'
28160 cls : 'btn-group roo-document-viewer-trash',
28164 cls : 'btn btn-default',
28165 html : '<i class="fa fa-trash"></i>'
28178 initEvents : function()
28180 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28181 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28183 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28184 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28186 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28187 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28189 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28190 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28192 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28193 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28195 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28196 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28198 this.bodyEl.on('click', this.onClick, this);
28199 this.downloadBtn.on('click', this.onDownload, this);
28200 this.trashBtn.on('click', this.onTrash, this);
28202 this.downloadBtn.hide();
28203 this.trashBtn.hide();
28205 if(this.showDownload){
28206 this.downloadBtn.show();
28209 if(this.showTrash){
28210 this.trashBtn.show();
28213 if(!this.showDownload && !this.showTrash) {
28214 this.footerEl.hide();
28219 initial : function()
28221 this.fireEvent('initial', this);
28225 onClick : function(e)
28227 e.preventDefault();
28229 this.fireEvent('click', this);
28232 onDownload : function(e)
28234 e.preventDefault();
28236 this.fireEvent('download', this);
28239 onTrash : function(e)
28241 e.preventDefault();
28243 this.fireEvent('trash', this);
28255 * @class Roo.bootstrap.NavProgressBar
28256 * @extends Roo.bootstrap.Component
28257 * Bootstrap NavProgressBar class
28260 * Create a new nav progress bar
28261 * @param {Object} config The config object
28264 Roo.bootstrap.NavProgressBar = function(config){
28265 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28267 this.bullets = this.bullets || [];
28269 // Roo.bootstrap.NavProgressBar.register(this);
28273 * Fires when the active item changes
28274 * @param {Roo.bootstrap.NavProgressBar} this
28275 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28276 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28283 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28288 getAutoCreate : function()
28290 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28294 cls : 'roo-navigation-bar-group',
28298 cls : 'roo-navigation-top-bar'
28302 cls : 'roo-navigation-bullets-bar',
28306 cls : 'roo-navigation-bar'
28313 cls : 'roo-navigation-bottom-bar'
28323 initEvents: function()
28328 onRender : function(ct, position)
28330 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28332 if(this.bullets.length){
28333 Roo.each(this.bullets, function(b){
28342 addItem : function(cfg)
28344 var item = new Roo.bootstrap.NavProgressItem(cfg);
28346 item.parentId = this.id;
28347 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28350 var top = new Roo.bootstrap.Element({
28352 cls : 'roo-navigation-bar-text'
28355 var bottom = new Roo.bootstrap.Element({
28357 cls : 'roo-navigation-bar-text'
28360 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28361 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28363 var topText = new Roo.bootstrap.Element({
28365 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28368 var bottomText = new Roo.bootstrap.Element({
28370 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28373 topText.onRender(top.el, null);
28374 bottomText.onRender(bottom.el, null);
28377 item.bottomEl = bottom;
28380 this.barItems.push(item);
28385 getActive : function()
28387 var active = false;
28389 Roo.each(this.barItems, function(v){
28391 if (!v.isActive()) {
28403 setActiveItem : function(item)
28407 Roo.each(this.barItems, function(v){
28408 if (v.rid == item.rid) {
28412 if (v.isActive()) {
28413 v.setActive(false);
28418 item.setActive(true);
28420 this.fireEvent('changed', this, item, prev);
28423 getBarItem: function(rid)
28427 Roo.each(this.barItems, function(e) {
28428 if (e.rid != rid) {
28439 indexOfItem : function(item)
28443 Roo.each(this.barItems, function(v, i){
28445 if (v.rid != item.rid) {
28456 setActiveNext : function()
28458 var i = this.indexOfItem(this.getActive());
28460 if (i > this.barItems.length) {
28464 this.setActiveItem(this.barItems[i+1]);
28467 setActivePrev : function()
28469 var i = this.indexOfItem(this.getActive());
28475 this.setActiveItem(this.barItems[i-1]);
28478 format : function()
28480 if(!this.barItems.length){
28484 var width = 100 / this.barItems.length;
28486 Roo.each(this.barItems, function(i){
28487 i.el.setStyle('width', width + '%');
28488 i.topEl.el.setStyle('width', width + '%');
28489 i.bottomEl.el.setStyle('width', width + '%');
28498 * Nav Progress Item
28503 * @class Roo.bootstrap.NavProgressItem
28504 * @extends Roo.bootstrap.Component
28505 * Bootstrap NavProgressItem class
28506 * @cfg {String} rid the reference id
28507 * @cfg {Boolean} active (true|false) Is item active default false
28508 * @cfg {Boolean} disabled (true|false) Is item active default false
28509 * @cfg {String} html
28510 * @cfg {String} position (top|bottom) text position default bottom
28511 * @cfg {String} icon show icon instead of number
28514 * Create a new NavProgressItem
28515 * @param {Object} config The config object
28517 Roo.bootstrap.NavProgressItem = function(config){
28518 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28523 * The raw click event for the entire grid.
28524 * @param {Roo.bootstrap.NavProgressItem} this
28525 * @param {Roo.EventObject} e
28532 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28538 position : 'bottom',
28541 getAutoCreate : function()
28543 var iconCls = 'roo-navigation-bar-item-icon';
28545 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28549 cls: 'roo-navigation-bar-item',
28559 cfg.cls += ' active';
28562 cfg.cls += ' disabled';
28568 disable : function()
28570 this.setDisabled(true);
28573 enable : function()
28575 this.setDisabled(false);
28578 initEvents: function()
28580 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28582 this.iconEl.on('click', this.onClick, this);
28585 onClick : function(e)
28587 e.preventDefault();
28593 if(this.fireEvent('click', this, e) === false){
28597 this.parent().setActiveItem(this);
28600 isActive: function ()
28602 return this.active;
28605 setActive : function(state)
28607 if(this.active == state){
28611 this.active = state;
28614 this.el.addClass('active');
28618 this.el.removeClass('active');
28623 setDisabled : function(state)
28625 if(this.disabled == state){
28629 this.disabled = state;
28632 this.el.addClass('disabled');
28636 this.el.removeClass('disabled');
28639 tooltipEl : function()
28641 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28654 * @class Roo.bootstrap.FieldLabel
28655 * @extends Roo.bootstrap.Component
28656 * Bootstrap FieldLabel class
28657 * @cfg {String} html contents of the element
28658 * @cfg {String} tag tag of the element default label
28659 * @cfg {String} cls class of the element
28660 * @cfg {String} target label target
28661 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28662 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28663 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28664 * @cfg {String} iconTooltip default "This field is required"
28667 * Create a new FieldLabel
28668 * @param {Object} config The config object
28671 Roo.bootstrap.FieldLabel = function(config){
28672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28677 * Fires after the field has been marked as invalid.
28678 * @param {Roo.form.FieldLabel} this
28679 * @param {String} msg The validation message
28684 * Fires after the field has been validated with no errors.
28685 * @param {Roo.form.FieldLabel} this
28691 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28698 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28699 validClass : 'text-success fa fa-lg fa-check',
28700 iconTooltip : 'This field is required',
28702 getAutoCreate : function(){
28706 cls : 'roo-bootstrap-field-label ' + this.cls,
28712 tooltip : this.iconTooltip
28724 initEvents: function()
28726 Roo.bootstrap.Element.superclass.initEvents.call(this);
28728 this.iconEl = this.el.select('i', true).first();
28730 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28732 Roo.bootstrap.FieldLabel.register(this);
28736 * Mark this field as valid
28738 markValid : function()
28740 this.iconEl.show();
28742 this.iconEl.removeClass(this.invalidClass);
28744 this.iconEl.addClass(this.validClass);
28746 this.fireEvent('valid', this);
28750 * Mark this field as invalid
28751 * @param {String} msg The validation message
28753 markInvalid : function(msg)
28755 this.iconEl.show();
28757 this.iconEl.removeClass(this.validClass);
28759 this.iconEl.addClass(this.invalidClass);
28761 this.fireEvent('invalid', this, msg);
28767 Roo.apply(Roo.bootstrap.FieldLabel, {
28772 * register a FieldLabel Group
28773 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28775 register : function(label)
28777 if(this.groups.hasOwnProperty(label.target)){
28781 this.groups[label.target] = label;
28785 * fetch a FieldLabel Group based on the target
28786 * @param {string} target
28787 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28789 get: function(target) {
28790 if (typeof(this.groups[target]) == 'undefined') {
28794 return this.groups[target] ;
28803 * page DateSplitField.
28809 * @class Roo.bootstrap.DateSplitField
28810 * @extends Roo.bootstrap.Component
28811 * Bootstrap DateSplitField class
28812 * @cfg {string} fieldLabel - the label associated
28813 * @cfg {Number} labelWidth set the width of label (0-12)
28814 * @cfg {String} labelAlign (top|left)
28815 * @cfg {Boolean} dayAllowBlank (true|false) default false
28816 * @cfg {Boolean} monthAllowBlank (true|false) default false
28817 * @cfg {Boolean} yearAllowBlank (true|false) default false
28818 * @cfg {string} dayPlaceholder
28819 * @cfg {string} monthPlaceholder
28820 * @cfg {string} yearPlaceholder
28821 * @cfg {string} dayFormat default 'd'
28822 * @cfg {string} monthFormat default 'm'
28823 * @cfg {string} yearFormat default 'Y'
28827 * Create a new DateSplitField
28828 * @param {Object} config The config object
28831 Roo.bootstrap.DateSplitField = function(config){
28832 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28838 * getting the data of years
28839 * @param {Roo.bootstrap.DateSplitField} this
28840 * @param {Object} years
28845 * getting the data of days
28846 * @param {Roo.bootstrap.DateSplitField} this
28847 * @param {Object} days
28852 * Fires after the field has been marked as invalid.
28853 * @param {Roo.form.Field} this
28854 * @param {String} msg The validation message
28859 * Fires after the field has been validated with no errors.
28860 * @param {Roo.form.Field} this
28866 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28869 labelAlign : 'top',
28871 dayAllowBlank : false,
28872 monthAllowBlank : false,
28873 yearAllowBlank : false,
28874 dayPlaceholder : '',
28875 monthPlaceholder : '',
28876 yearPlaceholder : '',
28880 isFormField : true,
28882 getAutoCreate : function()
28886 cls : 'row roo-date-split-field-group',
28891 cls : 'form-hidden-field roo-date-split-field-group-value',
28897 if(this.fieldLabel){
28900 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28904 html : this.fieldLabel
28910 Roo.each(['day', 'month', 'year'], function(t){
28913 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28920 inputEl: function ()
28922 return this.el.select('.roo-date-split-field-group-value', true).first();
28925 onRender : function(ct, position)
28929 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28931 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28933 this.dayField = new Roo.bootstrap.ComboBox({
28934 allowBlank : this.dayAllowBlank,
28935 alwaysQuery : true,
28936 displayField : 'value',
28939 forceSelection : true,
28941 placeholder : this.dayPlaceholder,
28942 selectOnFocus : true,
28943 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28944 triggerAction : 'all',
28946 valueField : 'value',
28947 store : new Roo.data.SimpleStore({
28948 data : (function() {
28950 _this.fireEvent('days', _this, days);
28953 fields : [ 'value' ]
28956 select : function (_self, record, index)
28958 _this.setValue(_this.getValue());
28963 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28965 this.monthField = new Roo.bootstrap.MonthField({
28966 after : '<i class=\"fa fa-calendar\"></i>',
28967 allowBlank : this.monthAllowBlank,
28968 placeholder : this.monthPlaceholder,
28971 render : function (_self)
28973 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28974 e.preventDefault();
28978 select : function (_self, oldvalue, newvalue)
28980 _this.setValue(_this.getValue());
28985 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28987 this.yearField = new Roo.bootstrap.ComboBox({
28988 allowBlank : this.yearAllowBlank,
28989 alwaysQuery : true,
28990 displayField : 'value',
28993 forceSelection : true,
28995 placeholder : this.yearPlaceholder,
28996 selectOnFocus : true,
28997 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28998 triggerAction : 'all',
29000 valueField : 'value',
29001 store : new Roo.data.SimpleStore({
29002 data : (function() {
29004 _this.fireEvent('years', _this, years);
29007 fields : [ 'value' ]
29010 select : function (_self, record, index)
29012 _this.setValue(_this.getValue());
29017 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29020 setValue : function(v, format)
29022 this.inputEl.dom.value = v;
29024 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29026 var d = Date.parseDate(v, f);
29033 this.setDay(d.format(this.dayFormat));
29034 this.setMonth(d.format(this.monthFormat));
29035 this.setYear(d.format(this.yearFormat));
29042 setDay : function(v)
29044 this.dayField.setValue(v);
29045 this.inputEl.dom.value = this.getValue();
29050 setMonth : function(v)
29052 this.monthField.setValue(v, true);
29053 this.inputEl.dom.value = this.getValue();
29058 setYear : function(v)
29060 this.yearField.setValue(v);
29061 this.inputEl.dom.value = this.getValue();
29066 getDay : function()
29068 return this.dayField.getValue();
29071 getMonth : function()
29073 return this.monthField.getValue();
29076 getYear : function()
29078 return this.yearField.getValue();
29081 getValue : function()
29083 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29085 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29095 this.inputEl.dom.value = '';
29100 validate : function()
29102 var d = this.dayField.validate();
29103 var m = this.monthField.validate();
29104 var y = this.yearField.validate();
29109 (!this.dayAllowBlank && !d) ||
29110 (!this.monthAllowBlank && !m) ||
29111 (!this.yearAllowBlank && !y)
29116 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29125 this.markInvalid();
29130 markValid : function()
29133 var label = this.el.select('label', true).first();
29134 var icon = this.el.select('i.fa-star', true).first();
29140 this.fireEvent('valid', this);
29144 * Mark this field as invalid
29145 * @param {String} msg The validation message
29147 markInvalid : function(msg)
29150 var label = this.el.select('label', true).first();
29151 var icon = this.el.select('i.fa-star', true).first();
29153 if(label && !icon){
29154 this.el.select('.roo-date-split-field-label', true).createChild({
29156 cls : 'text-danger fa fa-lg fa-star',
29157 tooltip : 'This field is required',
29158 style : 'margin-right:5px;'
29162 this.fireEvent('invalid', this, msg);
29165 clearInvalid : function()
29167 var label = this.el.select('label', true).first();
29168 var icon = this.el.select('i.fa-star', true).first();
29174 this.fireEvent('valid', this);
29177 getName: function()
29187 * http://masonry.desandro.com
29189 * The idea is to render all the bricks based on vertical width...
29191 * The original code extends 'outlayer' - we might need to use that....
29197 * @class Roo.bootstrap.LayoutMasonry
29198 * @extends Roo.bootstrap.Component
29199 * Bootstrap Layout Masonry class
29202 * Create a new Element
29203 * @param {Object} config The config object
29206 Roo.bootstrap.LayoutMasonry = function(config){
29207 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29213 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29216 * @cfg {Boolean} isLayoutInstant = no animation?
29218 isLayoutInstant : false, // needed?
29221 * @cfg {Number} boxWidth width of the columns
29226 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29231 * @cfg {Number} padWidth padding below box..
29236 * @cfg {Number} gutter gutter width..
29241 * @cfg {Number} maxCols maximum number of columns
29247 * @cfg {Boolean} isAutoInitial defalut true
29249 isAutoInitial : true,
29254 * @cfg {Boolean} isHorizontal defalut false
29256 isHorizontal : false,
29258 currentSize : null,
29264 bricks: null, //CompositeElement
29268 _isLayoutInited : false,
29270 // isAlternative : false, // only use for vertical layout...
29273 * @cfg {Number} alternativePadWidth padding below box..
29275 alternativePadWidth : 50,
29277 getAutoCreate : function(){
29281 cls: 'blog-masonary-wrapper ' + this.cls,
29283 cls : 'mas-boxes masonary'
29290 getChildContainer: function( )
29292 if (this.boxesEl) {
29293 return this.boxesEl;
29296 this.boxesEl = this.el.select('.mas-boxes').first();
29298 return this.boxesEl;
29302 initEvents : function()
29306 if(this.isAutoInitial){
29307 Roo.log('hook children rendered');
29308 this.on('childrenrendered', function() {
29309 Roo.log('children rendered');
29315 initial : function()
29317 this.currentSize = this.el.getBox(true);
29319 Roo.EventManager.onWindowResize(this.resize, this);
29321 if(!this.isAutoInitial){
29329 //this.layout.defer(500,this);
29333 resize : function()
29337 var cs = this.el.getBox(true);
29339 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29340 Roo.log("no change in with or X");
29344 this.currentSize = cs;
29350 layout : function()
29352 this._resetLayout();
29354 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29356 this.layoutItems( isInstant );
29358 this._isLayoutInited = true;
29362 _resetLayout : function()
29364 if(this.isHorizontal){
29365 this.horizontalMeasureColumns();
29369 this.verticalMeasureColumns();
29373 verticalMeasureColumns : function()
29375 this.getContainerWidth();
29377 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29378 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29382 var boxWidth = this.boxWidth + this.padWidth;
29384 if(this.containerWidth < this.boxWidth){
29385 boxWidth = this.containerWidth
29388 var containerWidth = this.containerWidth;
29390 var cols = Math.floor(containerWidth / boxWidth);
29392 this.cols = Math.max( cols, 1 );
29394 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29396 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29398 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29400 this.colWidth = boxWidth + avail - this.padWidth;
29402 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29403 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29406 horizontalMeasureColumns : function()
29408 this.getContainerWidth();
29410 var boxWidth = this.boxWidth;
29412 if(this.containerWidth < boxWidth){
29413 boxWidth = this.containerWidth;
29416 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29418 this.el.setHeight(boxWidth);
29422 getContainerWidth : function()
29424 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29427 layoutItems : function( isInstant )
29429 var items = Roo.apply([], this.bricks);
29431 if(this.isHorizontal){
29432 this._horizontalLayoutItems( items , isInstant );
29436 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29437 // this._verticalAlternativeLayoutItems( items , isInstant );
29441 this._verticalLayoutItems( items , isInstant );
29445 _verticalLayoutItems : function ( items , isInstant)
29447 if ( !items || !items.length ) {
29452 ['xs', 'xs', 'xs', 'tall'],
29453 ['xs', 'xs', 'tall'],
29454 ['xs', 'xs', 'sm'],
29455 ['xs', 'xs', 'xs'],
29461 ['sm', 'xs', 'xs'],
29465 ['tall', 'xs', 'xs', 'xs'],
29466 ['tall', 'xs', 'xs'],
29478 Roo.each(items, function(item, k){
29480 switch (item.size) {
29481 // these layouts take up a full box,
29492 boxes.push([item]);
29515 var filterPattern = function(box, length)
29523 var pattern = box.slice(0, length);
29527 Roo.each(pattern, function(i){
29528 format.push(i.size);
29531 Roo.each(standard, function(s){
29533 if(String(s) != String(format)){
29542 if(!match && length == 1){
29547 filterPattern(box, length - 1);
29551 queue.push(pattern);
29553 box = box.slice(length, box.length);
29555 filterPattern(box, 4);
29561 Roo.each(boxes, function(box, k){
29567 if(box.length == 1){
29572 filterPattern(box, 4);
29576 this._processVerticalLayoutQueue( queue, isInstant );
29580 // _verticalAlternativeLayoutItems : function( items , isInstant )
29582 // if ( !items || !items.length ) {
29586 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29590 _horizontalLayoutItems : function ( items , isInstant)
29592 if ( !items || !items.length || items.length < 3) {
29598 var eItems = items.slice(0, 3);
29600 items = items.slice(3, items.length);
29603 ['xs', 'xs', 'xs', 'wide'],
29604 ['xs', 'xs', 'wide'],
29605 ['xs', 'xs', 'sm'],
29606 ['xs', 'xs', 'xs'],
29612 ['sm', 'xs', 'xs'],
29616 ['wide', 'xs', 'xs', 'xs'],
29617 ['wide', 'xs', 'xs'],
29630 Roo.each(items, function(item, k){
29632 switch (item.size) {
29643 boxes.push([item]);
29667 var filterPattern = function(box, length)
29675 var pattern = box.slice(0, length);
29679 Roo.each(pattern, function(i){
29680 format.push(i.size);
29683 Roo.each(standard, function(s){
29685 if(String(s) != String(format)){
29694 if(!match && length == 1){
29699 filterPattern(box, length - 1);
29703 queue.push(pattern);
29705 box = box.slice(length, box.length);
29707 filterPattern(box, 4);
29713 Roo.each(boxes, function(box, k){
29719 if(box.length == 1){
29724 filterPattern(box, 4);
29731 var pos = this.el.getBox(true);
29735 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29737 var hit_end = false;
29739 Roo.each(queue, function(box){
29743 Roo.each(box, function(b){
29745 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29755 Roo.each(box, function(b){
29757 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29760 mx = Math.max(mx, b.x);
29764 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29768 Roo.each(box, function(b){
29770 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29784 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29787 /** Sets position of item in DOM
29788 * @param {Element} item
29789 * @param {Number} x - horizontal position
29790 * @param {Number} y - vertical position
29791 * @param {Boolean} isInstant - disables transitions
29793 _processVerticalLayoutQueue : function( queue, isInstant )
29795 var pos = this.el.getBox(true);
29800 for (var i = 0; i < this.cols; i++){
29804 Roo.each(queue, function(box, k){
29806 var col = k % this.cols;
29808 Roo.each(box, function(b,kk){
29810 b.el.position('absolute');
29812 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29813 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29815 if(b.size == 'md-left' || b.size == 'md-right'){
29816 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29817 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29820 b.el.setWidth(width);
29821 b.el.setHeight(height);
29823 b.el.select('iframe',true).setSize(width,height);
29827 for (var i = 0; i < this.cols; i++){
29829 if(maxY[i] < maxY[col]){
29834 col = Math.min(col, i);
29838 x = pos.x + col * (this.colWidth + this.padWidth);
29842 var positions = [];
29844 switch (box.length){
29846 positions = this.getVerticalOneBoxColPositions(x, y, box);
29849 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29852 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29855 positions = this.getVerticalFourBoxColPositions(x, y, box);
29861 Roo.each(box, function(b,kk){
29863 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29865 var sz = b.el.getSize();
29867 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29875 for (var i = 0; i < this.cols; i++){
29876 mY = Math.max(mY, maxY[i]);
29879 this.el.setHeight(mY - pos.y);
29883 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29885 // var pos = this.el.getBox(true);
29888 // var maxX = pos.right;
29890 // var maxHeight = 0;
29892 // Roo.each(items, function(item, k){
29896 // item.el.position('absolute');
29898 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29900 // item.el.setWidth(width);
29902 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29904 // item.el.setHeight(height);
29907 // item.el.setXY([x, y], isInstant ? false : true);
29909 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29912 // y = y + height + this.alternativePadWidth;
29914 // maxHeight = maxHeight + height + this.alternativePadWidth;
29918 // this.el.setHeight(maxHeight);
29922 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29924 var pos = this.el.getBox(true);
29929 var maxX = pos.right;
29931 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29933 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29935 Roo.each(queue, function(box, k){
29937 Roo.each(box, function(b, kk){
29939 b.el.position('absolute');
29941 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29942 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29944 if(b.size == 'md-left' || b.size == 'md-right'){
29945 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29946 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29949 b.el.setWidth(width);
29950 b.el.setHeight(height);
29958 var positions = [];
29960 switch (box.length){
29962 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29965 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29968 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29971 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29977 Roo.each(box, function(b,kk){
29979 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29981 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29989 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29991 Roo.each(eItems, function(b,k){
29993 b.size = (k == 0) ? 'sm' : 'xs';
29994 b.x = (k == 0) ? 2 : 1;
29995 b.y = (k == 0) ? 2 : 1;
29997 b.el.position('absolute');
29999 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30001 b.el.setWidth(width);
30003 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30005 b.el.setHeight(height);
30009 var positions = [];
30012 x : maxX - this.unitWidth * 2 - this.gutter,
30017 x : maxX - this.unitWidth,
30018 y : minY + (this.unitWidth + this.gutter) * 2
30022 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30026 Roo.each(eItems, function(b,k){
30028 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30034 getVerticalOneBoxColPositions : function(x, y, box)
30038 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30040 if(box[0].size == 'md-left'){
30044 if(box[0].size == 'md-right'){
30049 x : x + (this.unitWidth + this.gutter) * rand,
30056 getVerticalTwoBoxColPositions : function(x, y, box)
30060 if(box[0].size == 'xs'){
30064 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30068 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30082 x : x + (this.unitWidth + this.gutter) * 2,
30083 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30090 getVerticalThreeBoxColPositions : function(x, y, box)
30094 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30102 x : x + (this.unitWidth + this.gutter) * 1,
30107 x : x + (this.unitWidth + this.gutter) * 2,
30115 if(box[0].size == 'xs' && box[1].size == 'xs'){
30124 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30128 x : x + (this.unitWidth + this.gutter) * 1,
30142 x : x + (this.unitWidth + this.gutter) * 2,
30147 x : x + (this.unitWidth + this.gutter) * 2,
30148 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30155 getVerticalFourBoxColPositions : function(x, y, box)
30159 if(box[0].size == 'xs'){
30168 y : y + (this.unitHeight + this.gutter) * 1
30173 y : y + (this.unitHeight + this.gutter) * 2
30177 x : x + (this.unitWidth + this.gutter) * 1,
30191 x : x + (this.unitWidth + this.gutter) * 2,
30196 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30197 y : y + (this.unitHeight + this.gutter) * 1
30201 x : x + (this.unitWidth + this.gutter) * 2,
30202 y : y + (this.unitWidth + this.gutter) * 2
30209 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30213 if(box[0].size == 'md-left'){
30215 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30222 if(box[0].size == 'md-right'){
30224 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30225 y : minY + (this.unitWidth + this.gutter) * 1
30231 var rand = Math.floor(Math.random() * (4 - box[0].y));
30234 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30235 y : minY + (this.unitWidth + this.gutter) * rand
30242 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30246 if(box[0].size == 'xs'){
30249 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30254 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30255 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30263 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30268 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30269 y : minY + (this.unitWidth + this.gutter) * 2
30276 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30280 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30283 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30288 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30289 y : minY + (this.unitWidth + this.gutter) * 1
30293 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30294 y : minY + (this.unitWidth + this.gutter) * 2
30301 if(box[0].size == 'xs' && box[1].size == 'xs'){
30304 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30309 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30314 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30315 y : minY + (this.unitWidth + this.gutter) * 1
30323 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30328 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30329 y : minY + (this.unitWidth + this.gutter) * 2
30333 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30334 y : minY + (this.unitWidth + this.gutter) * 2
30341 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30345 if(box[0].size == 'xs'){
30348 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30353 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30358 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),
30363 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30364 y : minY + (this.unitWidth + this.gutter) * 1
30372 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30377 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30378 y : minY + (this.unitWidth + this.gutter) * 2
30382 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30383 y : minY + (this.unitWidth + this.gutter) * 2
30387 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),
30388 y : minY + (this.unitWidth + this.gutter) * 2
30402 * http://masonry.desandro.com
30404 * The idea is to render all the bricks based on vertical width...
30406 * The original code extends 'outlayer' - we might need to use that....
30412 * @class Roo.bootstrap.LayoutMasonryAuto
30413 * @extends Roo.bootstrap.Component
30414 * Bootstrap Layout Masonry class
30417 * Create a new Element
30418 * @param {Object} config The config object
30421 Roo.bootstrap.LayoutMasonryAuto = function(config){
30422 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30425 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30428 * @cfg {Boolean} isFitWidth - resize the width..
30430 isFitWidth : false, // options..
30432 * @cfg {Boolean} isOriginLeft = left align?
30434 isOriginLeft : true,
30436 * @cfg {Boolean} isOriginTop = top align?
30438 isOriginTop : false,
30440 * @cfg {Boolean} isLayoutInstant = no animation?
30442 isLayoutInstant : false, // needed?
30444 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30446 isResizingContainer : true,
30448 * @cfg {Number} columnWidth width of the columns
30454 * @cfg {Number} maxCols maximum number of columns
30459 * @cfg {Number} padHeight padding below box..
30465 * @cfg {Boolean} isAutoInitial defalut true
30468 isAutoInitial : true,
30474 initialColumnWidth : 0,
30475 currentSize : null,
30477 colYs : null, // array.
30484 bricks: null, //CompositeElement
30485 cols : 0, // array?
30486 // element : null, // wrapped now this.el
30487 _isLayoutInited : null,
30490 getAutoCreate : function(){
30494 cls: 'blog-masonary-wrapper ' + this.cls,
30496 cls : 'mas-boxes masonary'
30503 getChildContainer: function( )
30505 if (this.boxesEl) {
30506 return this.boxesEl;
30509 this.boxesEl = this.el.select('.mas-boxes').first();
30511 return this.boxesEl;
30515 initEvents : function()
30519 if(this.isAutoInitial){
30520 Roo.log('hook children rendered');
30521 this.on('childrenrendered', function() {
30522 Roo.log('children rendered');
30529 initial : function()
30531 this.reloadItems();
30533 this.currentSize = this.el.getBox(true);
30535 /// was window resize... - let's see if this works..
30536 Roo.EventManager.onWindowResize(this.resize, this);
30538 if(!this.isAutoInitial){
30543 this.layout.defer(500,this);
30546 reloadItems: function()
30548 this.bricks = this.el.select('.masonry-brick', true);
30550 this.bricks.each(function(b) {
30551 //Roo.log(b.getSize());
30552 if (!b.attr('originalwidth')) {
30553 b.attr('originalwidth', b.getSize().width);
30558 Roo.log(this.bricks.elements.length);
30561 resize : function()
30564 var cs = this.el.getBox(true);
30566 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30567 Roo.log("no change in with or X");
30570 this.currentSize = cs;
30574 layout : function()
30577 this._resetLayout();
30578 //this._manageStamps();
30580 // don't animate first layout
30581 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30582 this.layoutItems( isInstant );
30584 // flag for initalized
30585 this._isLayoutInited = true;
30588 layoutItems : function( isInstant )
30590 //var items = this._getItemsForLayout( this.items );
30591 // original code supports filtering layout items.. we just ignore it..
30593 this._layoutItems( this.bricks , isInstant );
30595 this._postLayout();
30597 _layoutItems : function ( items , isInstant)
30599 //this.fireEvent( 'layout', this, items );
30602 if ( !items || !items.elements.length ) {
30603 // no items, emit event with empty array
30608 items.each(function(item) {
30609 Roo.log("layout item");
30611 // get x/y object from method
30612 var position = this._getItemLayoutPosition( item );
30614 position.item = item;
30615 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30616 queue.push( position );
30619 this._processLayoutQueue( queue );
30621 /** Sets position of item in DOM
30622 * @param {Element} item
30623 * @param {Number} x - horizontal position
30624 * @param {Number} y - vertical position
30625 * @param {Boolean} isInstant - disables transitions
30627 _processLayoutQueue : function( queue )
30629 for ( var i=0, len = queue.length; i < len; i++ ) {
30630 var obj = queue[i];
30631 obj.item.position('absolute');
30632 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30638 * Any logic you want to do after each layout,
30639 * i.e. size the container
30641 _postLayout : function()
30643 this.resizeContainer();
30646 resizeContainer : function()
30648 if ( !this.isResizingContainer ) {
30651 var size = this._getContainerSize();
30653 this.el.setSize(size.width,size.height);
30654 this.boxesEl.setSize(size.width,size.height);
30660 _resetLayout : function()
30662 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30663 this.colWidth = this.el.getWidth();
30664 //this.gutter = this.el.getWidth();
30666 this.measureColumns();
30672 this.colYs.push( 0 );
30678 measureColumns : function()
30680 this.getContainerWidth();
30681 // if columnWidth is 0, default to outerWidth of first item
30682 if ( !this.columnWidth ) {
30683 var firstItem = this.bricks.first();
30684 Roo.log(firstItem);
30685 this.columnWidth = this.containerWidth;
30686 if (firstItem && firstItem.attr('originalwidth') ) {
30687 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30689 // columnWidth fall back to item of first element
30690 Roo.log("set column width?");
30691 this.initialColumnWidth = this.columnWidth ;
30693 // if first elem has no width, default to size of container
30698 if (this.initialColumnWidth) {
30699 this.columnWidth = this.initialColumnWidth;
30704 // column width is fixed at the top - however if container width get's smaller we should
30707 // this bit calcs how man columns..
30709 var columnWidth = this.columnWidth += this.gutter;
30711 // calculate columns
30712 var containerWidth = this.containerWidth + this.gutter;
30714 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30715 // fix rounding errors, typically with gutters
30716 var excess = columnWidth - containerWidth % columnWidth;
30719 // if overshoot is less than a pixel, round up, otherwise floor it
30720 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30721 cols = Math[ mathMethod ]( cols );
30722 this.cols = Math.max( cols, 1 );
30723 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30725 // padding positioning..
30726 var totalColWidth = this.cols * this.columnWidth;
30727 var padavail = this.containerWidth - totalColWidth;
30728 // so for 2 columns - we need 3 'pads'
30730 var padNeeded = (1+this.cols) * this.padWidth;
30732 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30734 this.columnWidth += padExtra
30735 //this.padWidth = Math.floor(padavail / ( this.cols));
30737 // adjust colum width so that padding is fixed??
30739 // we have 3 columns ... total = width * 3
30740 // we have X left over... that should be used by
30742 //if (this.expandC) {
30750 getContainerWidth : function()
30752 /* // container is parent if fit width
30753 var container = this.isFitWidth ? this.element.parentNode : this.element;
30754 // check that this.size and size are there
30755 // IE8 triggers resize on body size change, so they might not be
30757 var size = getSize( container ); //FIXME
30758 this.containerWidth = size && size.innerWidth; //FIXME
30761 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30765 _getItemLayoutPosition : function( item ) // what is item?
30767 // we resize the item to our columnWidth..
30769 item.setWidth(this.columnWidth);
30770 item.autoBoxAdjust = false;
30772 var sz = item.getSize();
30774 // how many columns does this brick span
30775 var remainder = this.containerWidth % this.columnWidth;
30777 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30778 // round if off by 1 pixel, otherwise use ceil
30779 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30780 colSpan = Math.min( colSpan, this.cols );
30782 // normally this should be '1' as we dont' currently allow multi width columns..
30784 var colGroup = this._getColGroup( colSpan );
30785 // get the minimum Y value from the columns
30786 var minimumY = Math.min.apply( Math, colGroup );
30787 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30789 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30791 // position the brick
30793 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30794 y: this.currentSize.y + minimumY + this.padHeight
30798 // apply setHeight to necessary columns
30799 var setHeight = minimumY + sz.height + this.padHeight;
30800 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30802 var setSpan = this.cols + 1 - colGroup.length;
30803 for ( var i = 0; i < setSpan; i++ ) {
30804 this.colYs[ shortColIndex + i ] = setHeight ;
30811 * @param {Number} colSpan - number of columns the element spans
30812 * @returns {Array} colGroup
30814 _getColGroup : function( colSpan )
30816 if ( colSpan < 2 ) {
30817 // if brick spans only one column, use all the column Ys
30822 // how many different places could this brick fit horizontally
30823 var groupCount = this.cols + 1 - colSpan;
30824 // for each group potential horizontal position
30825 for ( var i = 0; i < groupCount; i++ ) {
30826 // make an array of colY values for that one group
30827 var groupColYs = this.colYs.slice( i, i + colSpan );
30828 // and get the max value of the array
30829 colGroup[i] = Math.max.apply( Math, groupColYs );
30834 _manageStamp : function( stamp )
30836 var stampSize = stamp.getSize();
30837 var offset = stamp.getBox();
30838 // get the columns that this stamp affects
30839 var firstX = this.isOriginLeft ? offset.x : offset.right;
30840 var lastX = firstX + stampSize.width;
30841 var firstCol = Math.floor( firstX / this.columnWidth );
30842 firstCol = Math.max( 0, firstCol );
30844 var lastCol = Math.floor( lastX / this.columnWidth );
30845 // lastCol should not go over if multiple of columnWidth #425
30846 lastCol -= lastX % this.columnWidth ? 0 : 1;
30847 lastCol = Math.min( this.cols - 1, lastCol );
30849 // set colYs to bottom of the stamp
30850 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30853 for ( var i = firstCol; i <= lastCol; i++ ) {
30854 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30859 _getContainerSize : function()
30861 this.maxY = Math.max.apply( Math, this.colYs );
30866 if ( this.isFitWidth ) {
30867 size.width = this._getContainerFitWidth();
30873 _getContainerFitWidth : function()
30875 var unusedCols = 0;
30876 // count unused columns
30879 if ( this.colYs[i] !== 0 ) {
30884 // fit container to columns that have been used
30885 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30888 needsResizeLayout : function()
30890 var previousWidth = this.containerWidth;
30891 this.getContainerWidth();
30892 return previousWidth !== this.containerWidth;
30907 * @class Roo.bootstrap.MasonryBrick
30908 * @extends Roo.bootstrap.Component
30909 * Bootstrap MasonryBrick class
30912 * Create a new MasonryBrick
30913 * @param {Object} config The config object
30916 Roo.bootstrap.MasonryBrick = function(config){
30917 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30923 * When a MasonryBrick is clcik
30924 * @param {Roo.bootstrap.MasonryBrick} this
30925 * @param {Roo.EventObject} e
30931 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30934 * @cfg {String} title
30938 * @cfg {String} html
30942 * @cfg {String} bgimage
30946 * @cfg {String} videourl
30950 * @cfg {String} cls
30954 * @cfg {String} href
30958 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30963 * @cfg {String} (center|bottom) placetitle
30968 * @cfg {Boolean} isFitContainer defalut true
30970 isFitContainer : true,
30973 * @cfg {Boolean} preventDefault defalut false
30975 preventDefault : false,
30977 getAutoCreate : function()
30979 if(!this.isFitContainer){
30980 return this.getSplitAutoCreate();
30983 var cls = 'masonry-brick masonry-brick-full';
30985 if(this.href.length){
30986 cls += ' masonry-brick-link';
30989 if(this.bgimage.length){
30990 cls += ' masonry-brick-image';
30993 if(!this.html.length){
30994 cls += ' enable-mask';
30998 cls += ' masonry-' + this.size + '-brick';
31001 if(this.placetitle.length){
31003 switch (this.placetitle) {
31005 cls += ' masonry-center-title';
31008 cls += ' masonry-bottom-title';
31015 if(!this.html.length && !this.bgimage.length){
31016 cls += ' masonry-center-title';
31019 if(!this.html.length && this.bgimage.length){
31020 cls += ' masonry-bottom-title';
31025 cls += ' ' + this.cls;
31029 tag: (this.href.length) ? 'a' : 'div',
31034 cls: 'masonry-brick-paragraph',
31040 if(this.href.length){
31041 cfg.href = this.href;
31044 var cn = cfg.cn[0].cn;
31046 if(this.title.length){
31049 cls: 'masonry-brick-title',
31054 if(this.html.length){
31057 cls: 'masonry-brick-text',
31061 if (!this.title.length && !this.html.length) {
31062 cfg.cn[0].cls += ' hide';
31065 if(this.bgimage.length){
31068 cls: 'masonry-brick-image-view',
31073 if(this.videourl.length){
31074 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31075 // youtube support only?
31078 cls: 'masonry-brick-image-view',
31081 allowfullscreen : true
31089 cls: 'masonry-brick-mask'
31096 getSplitAutoCreate : function()
31098 var cls = 'masonry-brick masonry-brick-split';
31100 if(this.href.length){
31101 cls += ' masonry-brick-link';
31104 if(this.bgimage.length){
31105 cls += ' masonry-brick-image';
31109 cls += ' masonry-' + this.size + '-brick';
31112 switch (this.placetitle) {
31114 cls += ' masonry-center-title';
31117 cls += ' masonry-bottom-title';
31120 if(!this.bgimage.length){
31121 cls += ' masonry-center-title';
31124 if(this.bgimage.length){
31125 cls += ' masonry-bottom-title';
31131 cls += ' ' + this.cls;
31135 tag: (this.href.length) ? 'a' : 'div',
31140 cls: 'masonry-brick-split-head',
31144 cls: 'masonry-brick-paragraph',
31151 cls: 'masonry-brick-split-body',
31157 if(this.href.length){
31158 cfg.href = this.href;
31161 if(this.title.length){
31162 cfg.cn[0].cn[0].cn.push({
31164 cls: 'masonry-brick-title',
31169 if(this.html.length){
31170 cfg.cn[1].cn.push({
31172 cls: 'masonry-brick-text',
31177 if(this.bgimage.length){
31178 cfg.cn[0].cn.push({
31180 cls: 'masonry-brick-image-view',
31185 if(this.videourl.length){
31186 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31187 // youtube support only?
31188 cfg.cn[0].cn.cn.push({
31190 cls: 'masonry-brick-image-view',
31193 allowfullscreen : true
31200 initEvents: function()
31202 switch (this.size) {
31235 this.el.on('touchstart', this.onTouchStart, this);
31236 this.el.on('touchmove', this.onTouchMove, this);
31237 this.el.on('touchend', this.onTouchEnd, this);
31238 this.el.on('contextmenu', this.onContextMenu, this);
31240 this.el.on('mouseenter' ,this.enter, this);
31241 this.el.on('mouseleave', this.leave, this);
31242 this.el.on('click', this.onClick, this);
31245 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31246 this.parent().bricks.push(this);
31251 onClick: function(e, el)
31253 var time = this.endTimer - this.startTimer;
31257 e.preventDefault();
31262 if(!this.preventDefault){
31266 e.preventDefault();
31267 this.fireEvent('click', this);
31270 enter: function(e, el)
31272 e.preventDefault();
31274 if(!this.isFitContainer){
31278 if(this.bgimage.length && this.html.length){
31279 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31283 leave: function(e, el)
31285 e.preventDefault();
31287 if(!this.isFitContainer){
31291 if(this.bgimage.length && this.html.length){
31292 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31296 onTouchStart: function(e, el)
31298 // e.preventDefault();
31300 this.touchmoved = false;
31302 if(!this.isFitContainer){
31306 if(!this.bgimage.length || !this.html.length){
31310 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31312 this.timer = new Date().getTime();
31316 onTouchMove: function(e, el)
31318 this.touchmoved = true;
31321 onContextMenu : function(e,el)
31323 e.preventDefault();
31324 e.stopPropagation();
31328 onTouchEnd: function(e, el)
31330 // e.preventDefault();
31332 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31339 if(!this.bgimage.length || !this.html.length){
31341 if(this.href.length){
31342 window.location.href = this.href;
31348 if(!this.isFitContainer){
31352 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31354 window.location.href = this.href;
31369 * @class Roo.bootstrap.Brick
31370 * @extends Roo.bootstrap.Component
31371 * Bootstrap Brick class
31374 * Create a new Brick
31375 * @param {Object} config The config object
31378 Roo.bootstrap.Brick = function(config){
31379 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31385 * When a Brick is click
31386 * @param {Roo.bootstrap.Brick} this
31387 * @param {Roo.EventObject} e
31393 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31396 * @cfg {String} title
31400 * @cfg {String} html
31404 * @cfg {String} bgimage
31408 * @cfg {String} cls
31412 * @cfg {String} href
31416 * @cfg {String} video
31420 * @cfg {Boolean} square
31424 getAutoCreate : function()
31426 var cls = 'roo-brick';
31428 if(this.href.length){
31429 cls += ' roo-brick-link';
31432 if(this.bgimage.length){
31433 cls += ' roo-brick-image';
31436 if(!this.html.length && !this.bgimage.length){
31437 cls += ' roo-brick-center-title';
31440 if(!this.html.length && this.bgimage.length){
31441 cls += ' roo-brick-bottom-title';
31445 cls += ' ' + this.cls;
31449 tag: (this.href.length) ? 'a' : 'div',
31454 cls: 'roo-brick-paragraph',
31460 if(this.href.length){
31461 cfg.href = this.href;
31464 var cn = cfg.cn[0].cn;
31466 if(this.title.length){
31469 cls: 'roo-brick-title',
31474 if(this.html.length){
31477 cls: 'roo-brick-text',
31484 if(this.bgimage.length){
31487 cls: 'roo-brick-image-view',
31495 initEvents: function()
31497 if(this.title.length || this.html.length){
31498 this.el.on('mouseenter' ,this.enter, this);
31499 this.el.on('mouseleave', this.leave, this);
31503 Roo.EventManager.onWindowResize(this.resize, this);
31508 resize : function()
31510 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31512 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31514 if(this.bgimage.length){
31515 var image = this.el.select('.roo-brick-image-view', true).first();
31516 image.setWidth(paragraph.getWidth());
31517 image.setHeight(paragraph.getWidth());
31519 this.el.setHeight(paragraph.getWidth());
31525 enter: function(e, el)
31527 e.preventDefault();
31529 if(this.bgimage.length){
31530 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31531 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31535 leave: function(e, el)
31537 e.preventDefault();
31539 if(this.bgimage.length){
31540 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31541 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31557 * @class Roo.bootstrap.NumberField
31558 * @extends Roo.bootstrap.Input
31559 * Bootstrap NumberField class
31565 * Create a new NumberField
31566 * @param {Object} config The config object
31569 Roo.bootstrap.NumberField = function(config){
31570 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31573 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31576 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31578 allowDecimals : true,
31580 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31582 decimalSeparator : ".",
31584 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31586 decimalPrecision : 2,
31588 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31590 allowNegative : true,
31592 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31594 minValue : Number.NEGATIVE_INFINITY,
31596 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31598 maxValue : Number.MAX_VALUE,
31600 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31602 minText : "The minimum value for this field is {0}",
31604 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31606 maxText : "The maximum value for this field is {0}",
31608 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31609 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31611 nanText : "{0} is not a valid number",
31613 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31618 initEvents : function()
31620 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31622 var allowed = "0123456789";
31624 if(this.allowDecimals){
31625 allowed += this.decimalSeparator;
31628 if(this.allowNegative){
31632 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31634 var keyPress = function(e){
31636 var k = e.getKey();
31638 var c = e.getCharCode();
31641 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31642 allowed.indexOf(String.fromCharCode(c)) === -1
31648 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31652 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31657 this.el.on("keypress", keyPress, this);
31660 validateValue : function(value)
31663 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31667 var num = this.parseValue(value);
31670 this.markInvalid(String.format(this.nanText, value));
31674 if(num < this.minValue){
31675 this.markInvalid(String.format(this.minText, this.minValue));
31679 if(num > this.maxValue){
31680 this.markInvalid(String.format(this.maxText, this.maxValue));
31687 getValue : function()
31689 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31692 parseValue : function(value)
31694 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31695 return isNaN(value) ? '' : value;
31698 fixPrecision : function(value)
31700 var nan = isNaN(value);
31702 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31703 return nan ? '' : value;
31705 return parseFloat(value).toFixed(this.decimalPrecision);
31708 setValue : function(v)
31710 v = this.fixPrecision(v);
31711 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31714 decimalPrecisionFcn : function(v)
31716 return Math.floor(v);
31719 beforeBlur : function()
31725 var v = this.parseValue(this.getRawValue());
31740 * @class Roo.bootstrap.DocumentSlider
31741 * @extends Roo.bootstrap.Component
31742 * Bootstrap DocumentSlider class
31745 * Create a new DocumentViewer
31746 * @param {Object} config The config object
31749 Roo.bootstrap.DocumentSlider = function(config){
31750 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31757 * Fire after initEvent
31758 * @param {Roo.bootstrap.DocumentSlider} this
31763 * Fire after update
31764 * @param {Roo.bootstrap.DocumentSlider} this
31770 * @param {Roo.bootstrap.DocumentSlider} this
31776 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31782 getAutoCreate : function()
31786 cls : 'roo-document-slider',
31790 cls : 'roo-document-slider-header',
31794 cls : 'roo-document-slider-header-title'
31800 cls : 'roo-document-slider-body',
31804 cls : 'roo-document-slider-prev',
31808 cls : 'fa fa-chevron-left'
31814 cls : 'roo-document-slider-thumb',
31818 cls : 'roo-document-slider-image'
31824 cls : 'roo-document-slider-next',
31828 cls : 'fa fa-chevron-right'
31840 initEvents : function()
31842 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31843 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31845 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31846 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31848 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31849 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31851 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31852 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31854 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31855 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31857 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31858 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31860 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31861 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31863 this.thumbEl.on('click', this.onClick, this);
31865 this.prevIndicator.on('click', this.prev, this);
31867 this.nextIndicator.on('click', this.next, this);
31871 initial : function()
31873 if(this.files.length){
31874 this.indicator = 1;
31878 this.fireEvent('initial', this);
31881 update : function()
31883 this.imageEl.attr('src', this.files[this.indicator - 1]);
31885 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31887 this.prevIndicator.show();
31889 if(this.indicator == 1){
31890 this.prevIndicator.hide();
31893 this.nextIndicator.show();
31895 if(this.indicator == this.files.length){
31896 this.nextIndicator.hide();
31899 this.thumbEl.scrollTo('top');
31901 this.fireEvent('update', this);
31904 onClick : function(e)
31906 e.preventDefault();
31908 this.fireEvent('click', this);
31913 e.preventDefault();
31915 this.indicator = Math.max(1, this.indicator - 1);
31922 e.preventDefault();
31924 this.indicator = Math.min(this.files.length, this.indicator + 1);
31938 * @class Roo.bootstrap.RadioSet
31939 * @extends Roo.bootstrap.Input
31940 * Bootstrap RadioSet class
31941 * @cfg {String} indicatorpos (left|right) default left
31942 * @cfg {Boolean} inline (true|false) inline the element (default true)
31943 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31945 * Create a new RadioSet
31946 * @param {Object} config The config object
31949 Roo.bootstrap.RadioSet = function(config){
31951 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31955 Roo.bootstrap.RadioSet.register(this);
31960 * Fires when the element is checked or unchecked.
31961 * @param {Roo.bootstrap.RadioSet} this This radio
31962 * @param {Roo.bootstrap.Radio} item The checked item
31969 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
31979 indicatorpos : 'left',
31981 getAutoCreate : function()
31985 cls : 'roo-radio-set-label',
31989 html : this.fieldLabel
31994 if(this.indicatorpos == 'left'){
31997 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
31998 tooltip : 'This field is required'
32003 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32004 tooltip : 'This field is required'
32010 cls : 'roo-radio-set-items'
32013 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32015 if (align === 'left' && this.fieldLabel.length) {
32017 label.cls += ' col-md-' + this.labelWidth;
32020 cls : "col-md-" + (12 - this.labelWidth),
32029 cls : 'roo-radio-set',
32033 cls : 'roo-radio-set-input',
32036 value : this.value ? this.value : ''
32044 cfg.cls += ' roo-radio-set-inline';
32051 initEvents : function()
32053 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32054 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32056 this.indicatorEl().hide();
32058 this.originalValue = this.getValue();
32062 inputEl: function ()
32064 return this.el.select('.roo-radio-set-input', true).first();
32067 getChildContainer : function()
32069 return this.itemsEl;
32072 register : function(item)
32074 this.radioes.push(item);
32077 item.el.addClass('radio-inline');
32082 validate : function()
32086 Roo.each(this.radioes, function(i){
32095 if(this.disabled || this.allowBlank || valid){
32100 this.markInvalid();
32105 markValid : function()
32107 this.indicatorEl().hide();
32108 this.el.removeClass([this.invalidClass, this.validClass]);
32109 this.el.addClass(this.validClass);
32111 this.fireEvent('valid', this);
32114 markInvalid : function(msg)
32116 if(this.allowBlank || this.disabled){
32120 this.indicatorEl().show();
32121 this.el.removeClass([this.invalidClass, this.validClass]);
32122 this.el.addClass(this.invalidClass);
32124 this.fireEvent('invalid', this, msg);
32128 setValue : function(v, suppressEvent)
32130 Roo.each(this.radioes, function(i){
32133 i.el.removeClass('checked');
32137 i.el.addClass('checked');
32139 if(suppressEvent !== true){
32140 this.fireEvent('check', this, i);
32146 Roo.bootstrap.RadioSet.superclass.setValue.call(this)
32152 Roo.apply(Roo.bootstrap.RadioSet, {
32156 register : function(set)
32158 this.groups[set.name] = set;
32161 get: function(name)
32163 if (typeof(this.groups[name]) == 'undefined') {
32167 return this.groups[name] ;
32173 * Ext JS Library 1.1.1
32174 * Copyright(c) 2006-2007, Ext JS, LLC.
32176 * Originally Released Under LGPL - original licence link has changed is not relivant.
32179 * <script type="text/javascript">
32184 * @class Roo.bootstrap.SplitBar
32185 * @extends Roo.util.Observable
32186 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32190 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32191 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32192 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32193 split.minSize = 100;
32194 split.maxSize = 600;
32195 split.animate = true;
32196 split.on('moved', splitterMoved);
32199 * Create a new SplitBar
32200 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32201 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32202 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32203 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32204 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32205 position of the SplitBar).
32207 Roo.bootstrap.SplitBar = function(cfg){
32212 // dragElement : elm
32213 // resizingElement: el,
32215 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32216 // placement : Roo.bootstrap.SplitBar.LEFT ,
32217 // existingProxy ???
32220 this.el = Roo.get(cfg.dragElement, true);
32221 this.el.dom.unselectable = "on";
32223 this.resizingEl = Roo.get(cfg.resizingElement, true);
32227 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32228 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32231 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32234 * The minimum size of the resizing element. (Defaults to 0)
32240 * The maximum size of the resizing element. (Defaults to 2000)
32243 this.maxSize = 2000;
32246 * Whether to animate the transition to the new size
32249 this.animate = false;
32252 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32255 this.useShim = false;
32260 if(!cfg.existingProxy){
32262 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32264 this.proxy = Roo.get(cfg.existingProxy).dom;
32267 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32270 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32273 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32276 this.dragSpecs = {};
32279 * @private The adapter to use to positon and resize elements
32281 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32282 this.adapter.init(this);
32284 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32286 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32287 this.el.addClass("roo-splitbar-h");
32290 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32291 this.el.addClass("roo-splitbar-v");
32297 * Fires when the splitter is moved (alias for {@link #event-moved})
32298 * @param {Roo.bootstrap.SplitBar} this
32299 * @param {Number} newSize the new width or height
32304 * Fires when the splitter is moved
32305 * @param {Roo.bootstrap.SplitBar} this
32306 * @param {Number} newSize the new width or height
32310 * @event beforeresize
32311 * Fires before the splitter is dragged
32312 * @param {Roo.bootstrap.SplitBar} this
32314 "beforeresize" : true,
32316 "beforeapply" : true
32319 Roo.util.Observable.call(this);
32322 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32323 onStartProxyDrag : function(x, y){
32324 this.fireEvent("beforeresize", this);
32326 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32328 o.enableDisplayMode("block");
32329 // all splitbars share the same overlay
32330 Roo.bootstrap.SplitBar.prototype.overlay = o;
32332 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32333 this.overlay.show();
32334 Roo.get(this.proxy).setDisplayed("block");
32335 var size = this.adapter.getElementSize(this);
32336 this.activeMinSize = this.getMinimumSize();;
32337 this.activeMaxSize = this.getMaximumSize();;
32338 var c1 = size - this.activeMinSize;
32339 var c2 = Math.max(this.activeMaxSize - size, 0);
32340 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32341 this.dd.resetConstraints();
32342 this.dd.setXConstraint(
32343 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32344 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32346 this.dd.setYConstraint(0, 0);
32348 this.dd.resetConstraints();
32349 this.dd.setXConstraint(0, 0);
32350 this.dd.setYConstraint(
32351 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32352 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32355 this.dragSpecs.startSize = size;
32356 this.dragSpecs.startPoint = [x, y];
32357 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32361 * @private Called after the drag operation by the DDProxy
32363 onEndProxyDrag : function(e){
32364 Roo.get(this.proxy).setDisplayed(false);
32365 var endPoint = Roo.lib.Event.getXY(e);
32367 this.overlay.hide();
32370 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32371 newSize = this.dragSpecs.startSize +
32372 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32373 endPoint[0] - this.dragSpecs.startPoint[0] :
32374 this.dragSpecs.startPoint[0] - endPoint[0]
32377 newSize = this.dragSpecs.startSize +
32378 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32379 endPoint[1] - this.dragSpecs.startPoint[1] :
32380 this.dragSpecs.startPoint[1] - endPoint[1]
32383 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32384 if(newSize != this.dragSpecs.startSize){
32385 if(this.fireEvent('beforeapply', this, newSize) !== false){
32386 this.adapter.setElementSize(this, newSize);
32387 this.fireEvent("moved", this, newSize);
32388 this.fireEvent("resize", this, newSize);
32394 * Get the adapter this SplitBar uses
32395 * @return The adapter object
32397 getAdapter : function(){
32398 return this.adapter;
32402 * Set the adapter this SplitBar uses
32403 * @param {Object} adapter A SplitBar adapter object
32405 setAdapter : function(adapter){
32406 this.adapter = adapter;
32407 this.adapter.init(this);
32411 * Gets the minimum size for the resizing element
32412 * @return {Number} The minimum size
32414 getMinimumSize : function(){
32415 return this.minSize;
32419 * Sets the minimum size for the resizing element
32420 * @param {Number} minSize The minimum size
32422 setMinimumSize : function(minSize){
32423 this.minSize = minSize;
32427 * Gets the maximum size for the resizing element
32428 * @return {Number} The maximum size
32430 getMaximumSize : function(){
32431 return this.maxSize;
32435 * Sets the maximum size for the resizing element
32436 * @param {Number} maxSize The maximum size
32438 setMaximumSize : function(maxSize){
32439 this.maxSize = maxSize;
32443 * Sets the initialize size for the resizing element
32444 * @param {Number} size The initial size
32446 setCurrentSize : function(size){
32447 var oldAnimate = this.animate;
32448 this.animate = false;
32449 this.adapter.setElementSize(this, size);
32450 this.animate = oldAnimate;
32454 * Destroy this splitbar.
32455 * @param {Boolean} removeEl True to remove the element
32457 destroy : function(removeEl){
32459 this.shim.remove();
32462 this.proxy.parentNode.removeChild(this.proxy);
32470 * @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.
32472 Roo.bootstrap.SplitBar.createProxy = function(dir){
32473 var proxy = new Roo.Element(document.createElement("div"));
32474 proxy.unselectable();
32475 var cls = 'roo-splitbar-proxy';
32476 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32477 document.body.appendChild(proxy.dom);
32482 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32483 * Default Adapter. It assumes the splitter and resizing element are not positioned
32484 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32486 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32489 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32490 // do nothing for now
32491 init : function(s){
32495 * Called before drag operations to get the current size of the resizing element.
32496 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32498 getElementSize : function(s){
32499 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32500 return s.resizingEl.getWidth();
32502 return s.resizingEl.getHeight();
32507 * Called after drag operations to set the size of the resizing element.
32508 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32509 * @param {Number} newSize The new size to set
32510 * @param {Function} onComplete A function to be invoked when resizing is complete
32512 setElementSize : function(s, newSize, onComplete){
32513 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32515 s.resizingEl.setWidth(newSize);
32517 onComplete(s, newSize);
32520 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32525 s.resizingEl.setHeight(newSize);
32527 onComplete(s, newSize);
32530 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32537 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32538 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32539 * Adapter that moves the splitter element to align with the resized sizing element.
32540 * Used with an absolute positioned SplitBar.
32541 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32542 * document.body, make sure you assign an id to the body element.
32544 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32545 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32546 this.container = Roo.get(container);
32549 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32550 init : function(s){
32551 this.basic.init(s);
32554 getElementSize : function(s){
32555 return this.basic.getElementSize(s);
32558 setElementSize : function(s, newSize, onComplete){
32559 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32562 moveSplitter : function(s){
32563 var yes = Roo.bootstrap.SplitBar;
32564 switch(s.placement){
32566 s.el.setX(s.resizingEl.getRight());
32569 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32572 s.el.setY(s.resizingEl.getBottom());
32575 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32582 * Orientation constant - Create a vertical SplitBar
32586 Roo.bootstrap.SplitBar.VERTICAL = 1;
32589 * Orientation constant - Create a horizontal SplitBar
32593 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32596 * Placement constant - The resizing element is to the left of the splitter element
32600 Roo.bootstrap.SplitBar.LEFT = 1;
32603 * Placement constant - The resizing element is to the right of the splitter element
32607 Roo.bootstrap.SplitBar.RIGHT = 2;
32610 * Placement constant - The resizing element is positioned above the splitter element
32614 Roo.bootstrap.SplitBar.TOP = 3;
32617 * Placement constant - The resizing element is positioned under splitter element
32621 Roo.bootstrap.SplitBar.BOTTOM = 4;
32622 Roo.namespace("Roo.bootstrap.layout");/*
32624 * Ext JS Library 1.1.1
32625 * Copyright(c) 2006-2007, Ext JS, LLC.
32627 * Originally Released Under LGPL - original licence link has changed is not relivant.
32630 * <script type="text/javascript">
32634 * @class Roo.bootstrap.layout.Manager
32635 * @extends Roo.bootstrap.Component
32636 * Base class for layout managers.
32638 Roo.bootstrap.layout.Manager = function(config)
32640 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32646 /** false to disable window resize monitoring @type Boolean */
32647 this.monitorWindowResize = true;
32652 * Fires when a layout is performed.
32653 * @param {Roo.LayoutManager} this
32657 * @event regionresized
32658 * Fires when the user resizes a region.
32659 * @param {Roo.LayoutRegion} region The resized region
32660 * @param {Number} newSize The new size (width for east/west, height for north/south)
32662 "regionresized" : true,
32664 * @event regioncollapsed
32665 * Fires when a region is collapsed.
32666 * @param {Roo.LayoutRegion} region The collapsed region
32668 "regioncollapsed" : true,
32670 * @event regionexpanded
32671 * Fires when a region is expanded.
32672 * @param {Roo.LayoutRegion} region The expanded region
32674 "regionexpanded" : true
32676 this.updating = false;
32679 this.el = Roo.get(config.el);
32685 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32690 monitorWindowResize : true,
32696 onRender : function(ct, position)
32699 this.el = Roo.get(ct);
32702 //this.fireEvent('render',this);
32706 initEvents: function()
32710 // ie scrollbar fix
32711 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32712 document.body.scroll = "no";
32713 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32714 this.el.position('relative');
32716 this.id = this.el.id;
32717 this.el.addClass("roo-layout-container");
32718 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32719 if(this.el.dom != document.body ) {
32720 this.el.on('resize', this.layout,this);
32721 this.el.on('show', this.layout,this);
32727 * Returns true if this layout is currently being updated
32728 * @return {Boolean}
32730 isUpdating : function(){
32731 return this.updating;
32735 * Suspend the LayoutManager from doing auto-layouts while
32736 * making multiple add or remove calls
32738 beginUpdate : function(){
32739 this.updating = true;
32743 * Restore auto-layouts and optionally disable the manager from performing a layout
32744 * @param {Boolean} noLayout true to disable a layout update
32746 endUpdate : function(noLayout){
32747 this.updating = false;
32753 layout: function(){
32757 onRegionResized : function(region, newSize){
32758 this.fireEvent("regionresized", region, newSize);
32762 onRegionCollapsed : function(region){
32763 this.fireEvent("regioncollapsed", region);
32766 onRegionExpanded : function(region){
32767 this.fireEvent("regionexpanded", region);
32771 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32772 * performs box-model adjustments.
32773 * @return {Object} The size as an object {width: (the width), height: (the height)}
32775 getViewSize : function()
32778 if(this.el.dom != document.body){
32779 size = this.el.getSize();
32781 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32783 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32784 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32789 * Returns the Element this layout is bound to.
32790 * @return {Roo.Element}
32792 getEl : function(){
32797 * Returns the specified region.
32798 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32799 * @return {Roo.LayoutRegion}
32801 getRegion : function(target){
32802 return this.regions[target.toLowerCase()];
32805 onWindowResize : function(){
32806 if(this.monitorWindowResize){
32813 * Ext JS Library 1.1.1
32814 * Copyright(c) 2006-2007, Ext JS, LLC.
32816 * Originally Released Under LGPL - original licence link has changed is not relivant.
32819 * <script type="text/javascript">
32822 * @class Roo.bootstrap.layout.Border
32823 * @extends Roo.bootstrap.layout.Manager
32824 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32825 * please see: examples/bootstrap/nested.html<br><br>
32827 <b>The container the layout is rendered into can be either the body element or any other element.
32828 If it is not the body element, the container needs to either be an absolute positioned element,
32829 or you will need to add "position:relative" to the css of the container. You will also need to specify
32830 the container size if it is not the body element.</b>
32833 * Create a new Border
32834 * @param {Object} config Configuration options
32836 Roo.bootstrap.layout.Border = function(config){
32837 config = config || {};
32838 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32842 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32843 if(config[region]){
32844 config[region].region = region;
32845 this.addRegion(config[region]);
32851 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32853 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32855 * Creates and adds a new region if it doesn't already exist.
32856 * @param {String} target The target region key (north, south, east, west or center).
32857 * @param {Object} config The regions config object
32858 * @return {BorderLayoutRegion} The new region
32860 addRegion : function(config)
32862 if(!this.regions[config.region]){
32863 var r = this.factory(config);
32864 this.bindRegion(r);
32866 return this.regions[config.region];
32870 bindRegion : function(r){
32871 this.regions[r.config.region] = r;
32873 r.on("visibilitychange", this.layout, this);
32874 r.on("paneladded", this.layout, this);
32875 r.on("panelremoved", this.layout, this);
32876 r.on("invalidated", this.layout, this);
32877 r.on("resized", this.onRegionResized, this);
32878 r.on("collapsed", this.onRegionCollapsed, this);
32879 r.on("expanded", this.onRegionExpanded, this);
32883 * Performs a layout update.
32885 layout : function()
32887 if(this.updating) {
32891 // render all the rebions if they have not been done alreayd?
32892 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32893 if(this.regions[region] && !this.regions[region].bodyEl){
32894 this.regions[region].onRender(this.el)
32898 var size = this.getViewSize();
32899 var w = size.width;
32900 var h = size.height;
32905 //var x = 0, y = 0;
32907 var rs = this.regions;
32908 var north = rs["north"];
32909 var south = rs["south"];
32910 var west = rs["west"];
32911 var east = rs["east"];
32912 var center = rs["center"];
32913 //if(this.hideOnLayout){ // not supported anymore
32914 //c.el.setStyle("display", "none");
32916 if(north && north.isVisible()){
32917 var b = north.getBox();
32918 var m = north.getMargins();
32919 b.width = w - (m.left+m.right);
32922 centerY = b.height + b.y + m.bottom;
32923 centerH -= centerY;
32924 north.updateBox(this.safeBox(b));
32926 if(south && south.isVisible()){
32927 var b = south.getBox();
32928 var m = south.getMargins();
32929 b.width = w - (m.left+m.right);
32931 var totalHeight = (b.height + m.top + m.bottom);
32932 b.y = h - totalHeight + m.top;
32933 centerH -= totalHeight;
32934 south.updateBox(this.safeBox(b));
32936 if(west && west.isVisible()){
32937 var b = west.getBox();
32938 var m = west.getMargins();
32939 b.height = centerH - (m.top+m.bottom);
32941 b.y = centerY + m.top;
32942 var totalWidth = (b.width + m.left + m.right);
32943 centerX += totalWidth;
32944 centerW -= totalWidth;
32945 west.updateBox(this.safeBox(b));
32947 if(east && east.isVisible()){
32948 var b = east.getBox();
32949 var m = east.getMargins();
32950 b.height = centerH - (m.top+m.bottom);
32951 var totalWidth = (b.width + m.left + m.right);
32952 b.x = w - totalWidth + m.left;
32953 b.y = centerY + m.top;
32954 centerW -= totalWidth;
32955 east.updateBox(this.safeBox(b));
32958 var m = center.getMargins();
32960 x: centerX + m.left,
32961 y: centerY + m.top,
32962 width: centerW - (m.left+m.right),
32963 height: centerH - (m.top+m.bottom)
32965 //if(this.hideOnLayout){
32966 //center.el.setStyle("display", "block");
32968 center.updateBox(this.safeBox(centerBox));
32971 this.fireEvent("layout", this);
32975 safeBox : function(box){
32976 box.width = Math.max(0, box.width);
32977 box.height = Math.max(0, box.height);
32982 * Adds a ContentPanel (or subclass) to this layout.
32983 * @param {String} target The target region key (north, south, east, west or center).
32984 * @param {Roo.ContentPanel} panel The panel to add
32985 * @return {Roo.ContentPanel} The added panel
32987 add : function(target, panel){
32989 target = target.toLowerCase();
32990 return this.regions[target].add(panel);
32994 * Remove a ContentPanel (or subclass) to this layout.
32995 * @param {String} target The target region key (north, south, east, west or center).
32996 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32997 * @return {Roo.ContentPanel} The removed panel
32999 remove : function(target, panel){
33000 target = target.toLowerCase();
33001 return this.regions[target].remove(panel);
33005 * Searches all regions for a panel with the specified id
33006 * @param {String} panelId
33007 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33009 findPanel : function(panelId){
33010 var rs = this.regions;
33011 for(var target in rs){
33012 if(typeof rs[target] != "function"){
33013 var p = rs[target].getPanel(panelId);
33023 * Searches all regions for a panel with the specified id and activates (shows) it.
33024 * @param {String/ContentPanel} panelId The panels id or the panel itself
33025 * @return {Roo.ContentPanel} The shown panel or null
33027 showPanel : function(panelId) {
33028 var rs = this.regions;
33029 for(var target in rs){
33030 var r = rs[target];
33031 if(typeof r != "function"){
33032 if(r.hasPanel(panelId)){
33033 return r.showPanel(panelId);
33041 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33042 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33045 restoreState : function(provider){
33047 provider = Roo.state.Manager;
33049 var sm = new Roo.LayoutStateManager();
33050 sm.init(this, provider);
33056 * Adds a xtype elements to the layout.
33060 xtype : 'ContentPanel',
33067 xtype : 'NestedLayoutPanel',
33073 items : [ ... list of content panels or nested layout panels.. ]
33077 * @param {Object} cfg Xtype definition of item to add.
33079 addxtype : function(cfg)
33081 // basically accepts a pannel...
33082 // can accept a layout region..!?!?
33083 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33086 // theory? children can only be panels??
33088 //if (!cfg.xtype.match(/Panel$/)) {
33093 if (typeof(cfg.region) == 'undefined') {
33094 Roo.log("Failed to add Panel, region was not set");
33098 var region = cfg.region;
33104 xitems = cfg.items;
33111 case 'Content': // ContentPanel (el, cfg)
33112 case 'Scroll': // ContentPanel (el, cfg)
33114 cfg.autoCreate = true;
33115 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33117 // var el = this.el.createChild();
33118 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33121 this.add(region, ret);
33125 case 'TreePanel': // our new panel!
33126 cfg.el = this.el.createChild();
33127 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33128 this.add(region, ret);
33133 // create a new Layout (which is a Border Layout...
33135 var clayout = cfg.layout;
33136 clayout.el = this.el.createChild();
33137 clayout.items = clayout.items || [];
33141 // replace this exitems with the clayout ones..
33142 xitems = clayout.items;
33144 // force background off if it's in center...
33145 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33146 cfg.background = false;
33148 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33151 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33152 //console.log('adding nested layout panel ' + cfg.toSource());
33153 this.add(region, ret);
33154 nb = {}; /// find first...
33159 // needs grid and region
33161 //var el = this.getRegion(region).el.createChild();
33163 *var el = this.el.createChild();
33164 // create the grid first...
33165 cfg.grid.container = el;
33166 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33169 if (region == 'center' && this.active ) {
33170 cfg.background = false;
33173 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33175 this.add(region, ret);
33177 if (cfg.background) {
33178 // render grid on panel activation (if panel background)
33179 ret.on('activate', function(gp) {
33180 if (!gp.grid.rendered) {
33181 // gp.grid.render(el);
33185 // cfg.grid.render(el);
33191 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33192 // it was the old xcomponent building that caused this before.
33193 // espeically if border is the top element in the tree.
33203 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33205 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33206 this.add(region, ret);
33210 throw "Can not add '" + cfg.xtype + "' to Border";
33216 this.beginUpdate();
33220 Roo.each(xitems, function(i) {
33221 region = nb && i.region ? i.region : false;
33223 var add = ret.addxtype(i);
33226 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33227 if (!i.background) {
33228 abn[region] = nb[region] ;
33235 // make the last non-background panel active..
33236 //if (nb) { Roo.log(abn); }
33239 for(var r in abn) {
33240 region = this.getRegion(r);
33242 // tried using nb[r], but it does not work..
33244 region.showPanel(abn[r]);
33255 factory : function(cfg)
33258 var validRegions = Roo.bootstrap.layout.Border.regions;
33260 var target = cfg.region;
33263 var r = Roo.bootstrap.layout;
33267 return new r.North(cfg);
33269 return new r.South(cfg);
33271 return new r.East(cfg);
33273 return new r.West(cfg);
33275 return new r.Center(cfg);
33277 throw 'Layout region "'+target+'" not supported.';
33284 * Ext JS Library 1.1.1
33285 * Copyright(c) 2006-2007, Ext JS, LLC.
33287 * Originally Released Under LGPL - original licence link has changed is not relivant.
33290 * <script type="text/javascript">
33294 * @class Roo.bootstrap.layout.Basic
33295 * @extends Roo.util.Observable
33296 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33297 * and does not have a titlebar, tabs or any other features. All it does is size and position
33298 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33299 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33300 * @cfg {string} region the region that it inhabits..
33301 * @cfg {bool} skipConfig skip config?
33305 Roo.bootstrap.layout.Basic = function(config){
33307 this.mgr = config.mgr;
33309 this.position = config.region;
33311 var skipConfig = config.skipConfig;
33315 * @scope Roo.BasicLayoutRegion
33319 * @event beforeremove
33320 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33321 * @param {Roo.LayoutRegion} this
33322 * @param {Roo.ContentPanel} panel The panel
33323 * @param {Object} e The cancel event object
33325 "beforeremove" : true,
33327 * @event invalidated
33328 * Fires when the layout for this region is changed.
33329 * @param {Roo.LayoutRegion} this
33331 "invalidated" : true,
33333 * @event visibilitychange
33334 * Fires when this region is shown or hidden
33335 * @param {Roo.LayoutRegion} this
33336 * @param {Boolean} visibility true or false
33338 "visibilitychange" : true,
33340 * @event paneladded
33341 * Fires when a panel is added.
33342 * @param {Roo.LayoutRegion} this
33343 * @param {Roo.ContentPanel} panel The panel
33345 "paneladded" : true,
33347 * @event panelremoved
33348 * Fires when a panel is removed.
33349 * @param {Roo.LayoutRegion} this
33350 * @param {Roo.ContentPanel} panel The panel
33352 "panelremoved" : true,
33354 * @event beforecollapse
33355 * Fires when this region before collapse.
33356 * @param {Roo.LayoutRegion} this
33358 "beforecollapse" : true,
33361 * Fires when this region is collapsed.
33362 * @param {Roo.LayoutRegion} this
33364 "collapsed" : true,
33367 * Fires when this region is expanded.
33368 * @param {Roo.LayoutRegion} this
33373 * Fires when this region is slid into view.
33374 * @param {Roo.LayoutRegion} this
33376 "slideshow" : true,
33379 * Fires when this region slides out of view.
33380 * @param {Roo.LayoutRegion} this
33382 "slidehide" : true,
33384 * @event panelactivated
33385 * Fires when a panel is activated.
33386 * @param {Roo.LayoutRegion} this
33387 * @param {Roo.ContentPanel} panel The activated panel
33389 "panelactivated" : true,
33392 * Fires when the user resizes this region.
33393 * @param {Roo.LayoutRegion} this
33394 * @param {Number} newSize The new size (width for east/west, height for north/south)
33398 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33399 this.panels = new Roo.util.MixedCollection();
33400 this.panels.getKey = this.getPanelId.createDelegate(this);
33402 this.activePanel = null;
33403 // ensure listeners are added...
33405 if (config.listeners || config.events) {
33406 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33407 listeners : config.listeners || {},
33408 events : config.events || {}
33412 if(skipConfig !== true){
33413 this.applyConfig(config);
33417 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33419 getPanelId : function(p){
33423 applyConfig : function(config){
33424 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33425 this.config = config;
33430 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33431 * the width, for horizontal (north, south) the height.
33432 * @param {Number} newSize The new width or height
33434 resizeTo : function(newSize){
33435 var el = this.el ? this.el :
33436 (this.activePanel ? this.activePanel.getEl() : null);
33438 switch(this.position){
33441 el.setWidth(newSize);
33442 this.fireEvent("resized", this, newSize);
33446 el.setHeight(newSize);
33447 this.fireEvent("resized", this, newSize);
33453 getBox : function(){
33454 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33457 getMargins : function(){
33458 return this.margins;
33461 updateBox : function(box){
33463 var el = this.activePanel.getEl();
33464 el.dom.style.left = box.x + "px";
33465 el.dom.style.top = box.y + "px";
33466 this.activePanel.setSize(box.width, box.height);
33470 * Returns the container element for this region.
33471 * @return {Roo.Element}
33473 getEl : function(){
33474 return this.activePanel;
33478 * Returns true if this region is currently visible.
33479 * @return {Boolean}
33481 isVisible : function(){
33482 return this.activePanel ? true : false;
33485 setActivePanel : function(panel){
33486 panel = this.getPanel(panel);
33487 if(this.activePanel && this.activePanel != panel){
33488 this.activePanel.setActiveState(false);
33489 this.activePanel.getEl().setLeftTop(-10000,-10000);
33491 this.activePanel = panel;
33492 panel.setActiveState(true);
33494 panel.setSize(this.box.width, this.box.height);
33496 this.fireEvent("panelactivated", this, panel);
33497 this.fireEvent("invalidated");
33501 * Show the specified panel.
33502 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33503 * @return {Roo.ContentPanel} The shown panel or null
33505 showPanel : function(panel){
33506 panel = this.getPanel(panel);
33508 this.setActivePanel(panel);
33514 * Get the active panel for this region.
33515 * @return {Roo.ContentPanel} The active panel or null
33517 getActivePanel : function(){
33518 return this.activePanel;
33522 * Add the passed ContentPanel(s)
33523 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33524 * @return {Roo.ContentPanel} The panel added (if only one was added)
33526 add : function(panel){
33527 if(arguments.length > 1){
33528 for(var i = 0, len = arguments.length; i < len; i++) {
33529 this.add(arguments[i]);
33533 if(this.hasPanel(panel)){
33534 this.showPanel(panel);
33537 var el = panel.getEl();
33538 if(el.dom.parentNode != this.mgr.el.dom){
33539 this.mgr.el.dom.appendChild(el.dom);
33541 if(panel.setRegion){
33542 panel.setRegion(this);
33544 this.panels.add(panel);
33545 el.setStyle("position", "absolute");
33546 if(!panel.background){
33547 this.setActivePanel(panel);
33548 if(this.config.initialSize && this.panels.getCount()==1){
33549 this.resizeTo(this.config.initialSize);
33552 this.fireEvent("paneladded", this, panel);
33557 * Returns true if the panel is in this region.
33558 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33559 * @return {Boolean}
33561 hasPanel : function(panel){
33562 if(typeof panel == "object"){ // must be panel obj
33563 panel = panel.getId();
33565 return this.getPanel(panel) ? true : false;
33569 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33570 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33571 * @param {Boolean} preservePanel Overrides the config preservePanel option
33572 * @return {Roo.ContentPanel} The panel that was removed
33574 remove : function(panel, preservePanel){
33575 panel = this.getPanel(panel);
33580 this.fireEvent("beforeremove", this, panel, e);
33581 if(e.cancel === true){
33584 var panelId = panel.getId();
33585 this.panels.removeKey(panelId);
33590 * Returns the panel specified or null if it's not in this region.
33591 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33592 * @return {Roo.ContentPanel}
33594 getPanel : function(id){
33595 if(typeof id == "object"){ // must be panel obj
33598 return this.panels.get(id);
33602 * Returns this regions position (north/south/east/west/center).
33605 getPosition: function(){
33606 return this.position;
33610 * Ext JS Library 1.1.1
33611 * Copyright(c) 2006-2007, Ext JS, LLC.
33613 * Originally Released Under LGPL - original licence link has changed is not relivant.
33616 * <script type="text/javascript">
33620 * @class Roo.bootstrap.layout.Region
33621 * @extends Roo.bootstrap.layout.Basic
33622 * This class represents a region in a layout manager.
33624 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33625 * @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})
33626 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33627 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33628 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33629 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33630 * @cfg {String} title The title for the region (overrides panel titles)
33631 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33632 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33633 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33634 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33635 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33636 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33637 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33638 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33639 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33640 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33642 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33643 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33644 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33645 * @cfg {Number} width For East/West panels
33646 * @cfg {Number} height For North/South panels
33647 * @cfg {Boolean} split To show the splitter
33648 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33650 * @cfg {string} cls Extra CSS classes to add to region
33652 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33653 * @cfg {string} region the region that it inhabits..
33656 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33657 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33659 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33660 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33661 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33663 Roo.bootstrap.layout.Region = function(config)
33665 this.applyConfig(config);
33667 var mgr = config.mgr;
33668 var pos = config.region;
33669 config.skipConfig = true;
33670 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33673 this.onRender(mgr.el);
33676 this.visible = true;
33677 this.collapsed = false;
33678 this.unrendered_panels = [];
33681 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33683 position: '', // set by wrapper (eg. north/south etc..)
33684 unrendered_panels : null, // unrendered panels.
33685 createBody : function(){
33686 /** This region's body element
33687 * @type Roo.Element */
33688 this.bodyEl = this.el.createChild({
33690 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33694 onRender: function(ctr, pos)
33696 var dh = Roo.DomHelper;
33697 /** This region's container element
33698 * @type Roo.Element */
33699 this.el = dh.append(ctr.dom, {
33701 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33703 /** This region's title element
33704 * @type Roo.Element */
33706 this.titleEl = dh.append(this.el.dom,
33709 unselectable: "on",
33710 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33712 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33713 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33716 this.titleEl.enableDisplayMode();
33717 /** This region's title text element
33718 * @type HTMLElement */
33719 this.titleTextEl = this.titleEl.dom.firstChild;
33720 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33722 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33723 this.closeBtn.enableDisplayMode();
33724 this.closeBtn.on("click", this.closeClicked, this);
33725 this.closeBtn.hide();
33727 this.createBody(this.config);
33728 if(this.config.hideWhenEmpty){
33730 this.on("paneladded", this.validateVisibility, this);
33731 this.on("panelremoved", this.validateVisibility, this);
33733 if(this.autoScroll){
33734 this.bodyEl.setStyle("overflow", "auto");
33736 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33738 //if(c.titlebar !== false){
33739 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33740 this.titleEl.hide();
33742 this.titleEl.show();
33743 if(this.config.title){
33744 this.titleTextEl.innerHTML = this.config.title;
33748 if(this.config.collapsed){
33749 this.collapse(true);
33751 if(this.config.hidden){
33755 if (this.unrendered_panels && this.unrendered_panels.length) {
33756 for (var i =0;i< this.unrendered_panels.length; i++) {
33757 this.add(this.unrendered_panels[i]);
33759 this.unrendered_panels = null;
33765 applyConfig : function(c)
33768 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33769 var dh = Roo.DomHelper;
33770 if(c.titlebar !== false){
33771 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33772 this.collapseBtn.on("click", this.collapse, this);
33773 this.collapseBtn.enableDisplayMode();
33775 if(c.showPin === true || this.showPin){
33776 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33777 this.stickBtn.enableDisplayMode();
33778 this.stickBtn.on("click", this.expand, this);
33779 this.stickBtn.hide();
33784 /** This region's collapsed element
33785 * @type Roo.Element */
33788 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33789 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33792 if(c.floatable !== false){
33793 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33794 this.collapsedEl.on("click", this.collapseClick, this);
33797 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33798 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33799 id: "message", unselectable: "on", style:{"float":"left"}});
33800 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33802 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33803 this.expandBtn.on("click", this.expand, this);
33807 if(this.collapseBtn){
33808 this.collapseBtn.setVisible(c.collapsible == true);
33811 this.cmargins = c.cmargins || this.cmargins ||
33812 (this.position == "west" || this.position == "east" ?
33813 {top: 0, left: 2, right:2, bottom: 0} :
33814 {top: 2, left: 0, right:0, bottom: 2});
33816 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33819 this.bottomTabs = c.tabPosition != "top";
33821 this.autoScroll = c.autoScroll || false;
33826 this.duration = c.duration || .30;
33827 this.slideDuration = c.slideDuration || .45;
33832 * Returns true if this region is currently visible.
33833 * @return {Boolean}
33835 isVisible : function(){
33836 return this.visible;
33840 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33841 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33843 //setCollapsedTitle : function(title){
33844 // title = title || " ";
33845 // if(this.collapsedTitleTextEl){
33846 // this.collapsedTitleTextEl.innerHTML = title;
33850 getBox : function(){
33852 // if(!this.collapsed){
33853 b = this.el.getBox(false, true);
33855 // b = this.collapsedEl.getBox(false, true);
33860 getMargins : function(){
33861 return this.margins;
33862 //return this.collapsed ? this.cmargins : this.margins;
33865 highlight : function(){
33866 this.el.addClass("x-layout-panel-dragover");
33869 unhighlight : function(){
33870 this.el.removeClass("x-layout-panel-dragover");
33873 updateBox : function(box)
33875 if (!this.bodyEl) {
33876 return; // not rendered yet..
33880 if(!this.collapsed){
33881 this.el.dom.style.left = box.x + "px";
33882 this.el.dom.style.top = box.y + "px";
33883 this.updateBody(box.width, box.height);
33885 this.collapsedEl.dom.style.left = box.x + "px";
33886 this.collapsedEl.dom.style.top = box.y + "px";
33887 this.collapsedEl.setSize(box.width, box.height);
33890 this.tabs.autoSizeTabs();
33894 updateBody : function(w, h)
33897 this.el.setWidth(w);
33898 w -= this.el.getBorderWidth("rl");
33899 if(this.config.adjustments){
33900 w += this.config.adjustments[0];
33903 if(h !== null && h > 0){
33904 this.el.setHeight(h);
33905 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33906 h -= this.el.getBorderWidth("tb");
33907 if(this.config.adjustments){
33908 h += this.config.adjustments[1];
33910 this.bodyEl.setHeight(h);
33912 h = this.tabs.syncHeight(h);
33915 if(this.panelSize){
33916 w = w !== null ? w : this.panelSize.width;
33917 h = h !== null ? h : this.panelSize.height;
33919 if(this.activePanel){
33920 var el = this.activePanel.getEl();
33921 w = w !== null ? w : el.getWidth();
33922 h = h !== null ? h : el.getHeight();
33923 this.panelSize = {width: w, height: h};
33924 this.activePanel.setSize(w, h);
33926 if(Roo.isIE && this.tabs){
33927 this.tabs.el.repaint();
33932 * Returns the container element for this region.
33933 * @return {Roo.Element}
33935 getEl : function(){
33940 * Hides this region.
33943 //if(!this.collapsed){
33944 this.el.dom.style.left = "-2000px";
33947 // this.collapsedEl.dom.style.left = "-2000px";
33948 // this.collapsedEl.hide();
33950 this.visible = false;
33951 this.fireEvent("visibilitychange", this, false);
33955 * Shows this region if it was previously hidden.
33958 //if(!this.collapsed){
33961 // this.collapsedEl.show();
33963 this.visible = true;
33964 this.fireEvent("visibilitychange", this, true);
33967 closeClicked : function(){
33968 if(this.activePanel){
33969 this.remove(this.activePanel);
33973 collapseClick : function(e){
33975 e.stopPropagation();
33978 e.stopPropagation();
33984 * Collapses this region.
33985 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33988 collapse : function(skipAnim, skipCheck = false){
33989 if(this.collapsed) {
33993 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33995 this.collapsed = true;
33997 this.split.el.hide();
33999 if(this.config.animate && skipAnim !== true){
34000 this.fireEvent("invalidated", this);
34001 this.animateCollapse();
34003 this.el.setLocation(-20000,-20000);
34005 this.collapsedEl.show();
34006 this.fireEvent("collapsed", this);
34007 this.fireEvent("invalidated", this);
34013 animateCollapse : function(){
34018 * Expands this region if it was previously collapsed.
34019 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34020 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34023 expand : function(e, skipAnim){
34025 e.stopPropagation();
34027 if(!this.collapsed || this.el.hasActiveFx()) {
34031 this.afterSlideIn();
34034 this.collapsed = false;
34035 if(this.config.animate && skipAnim !== true){
34036 this.animateExpand();
34040 this.split.el.show();
34042 this.collapsedEl.setLocation(-2000,-2000);
34043 this.collapsedEl.hide();
34044 this.fireEvent("invalidated", this);
34045 this.fireEvent("expanded", this);
34049 animateExpand : function(){
34053 initTabs : function()
34055 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34057 var ts = new Roo.bootstrap.panel.Tabs({
34058 el: this.bodyEl.dom,
34059 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34060 disableTooltips: this.config.disableTabTips,
34061 toolbar : this.config.toolbar
34064 if(this.config.hideTabs){
34065 ts.stripWrap.setDisplayed(false);
34068 ts.resizeTabs = this.config.resizeTabs === true;
34069 ts.minTabWidth = this.config.minTabWidth || 40;
34070 ts.maxTabWidth = this.config.maxTabWidth || 250;
34071 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34072 ts.monitorResize = false;
34073 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34074 ts.bodyEl.addClass('roo-layout-tabs-body');
34075 this.panels.each(this.initPanelAsTab, this);
34078 initPanelAsTab : function(panel){
34079 var ti = this.tabs.addTab(
34083 this.config.closeOnTab && panel.isClosable(),
34086 if(panel.tabTip !== undefined){
34087 ti.setTooltip(panel.tabTip);
34089 ti.on("activate", function(){
34090 this.setActivePanel(panel);
34093 if(this.config.closeOnTab){
34094 ti.on("beforeclose", function(t, e){
34096 this.remove(panel);
34100 panel.tabItem = ti;
34105 updatePanelTitle : function(panel, title)
34107 if(this.activePanel == panel){
34108 this.updateTitle(title);
34111 var ti = this.tabs.getTab(panel.getEl().id);
34113 if(panel.tabTip !== undefined){
34114 ti.setTooltip(panel.tabTip);
34119 updateTitle : function(title){
34120 if(this.titleTextEl && !this.config.title){
34121 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34125 setActivePanel : function(panel)
34127 panel = this.getPanel(panel);
34128 if(this.activePanel && this.activePanel != panel){
34129 this.activePanel.setActiveState(false);
34131 this.activePanel = panel;
34132 panel.setActiveState(true);
34133 if(this.panelSize){
34134 panel.setSize(this.panelSize.width, this.panelSize.height);
34137 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34139 this.updateTitle(panel.getTitle());
34141 this.fireEvent("invalidated", this);
34143 this.fireEvent("panelactivated", this, panel);
34147 * Shows the specified panel.
34148 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34149 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34151 showPanel : function(panel)
34153 panel = this.getPanel(panel);
34156 var tab = this.tabs.getTab(panel.getEl().id);
34157 if(tab.isHidden()){
34158 this.tabs.unhideTab(tab.id);
34162 this.setActivePanel(panel);
34169 * Get the active panel for this region.
34170 * @return {Roo.ContentPanel} The active panel or null
34172 getActivePanel : function(){
34173 return this.activePanel;
34176 validateVisibility : function(){
34177 if(this.panels.getCount() < 1){
34178 this.updateTitle(" ");
34179 this.closeBtn.hide();
34182 if(!this.isVisible()){
34189 * Adds the passed ContentPanel(s) to this region.
34190 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34191 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34193 add : function(panel)
34195 if(arguments.length > 1){
34196 for(var i = 0, len = arguments.length; i < len; i++) {
34197 this.add(arguments[i]);
34202 // if we have not been rendered yet, then we can not really do much of this..
34203 if (!this.bodyEl) {
34204 this.unrendered_panels.push(panel);
34211 if(this.hasPanel(panel)){
34212 this.showPanel(panel);
34215 panel.setRegion(this);
34216 this.panels.add(panel);
34217 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34218 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34219 // and hide them... ???
34220 this.bodyEl.dom.appendChild(panel.getEl().dom);
34221 if(panel.background !== true){
34222 this.setActivePanel(panel);
34224 this.fireEvent("paneladded", this, panel);
34231 this.initPanelAsTab(panel);
34235 if(panel.background !== true){
34236 this.tabs.activate(panel.getEl().id);
34238 this.fireEvent("paneladded", this, panel);
34243 * Hides the tab for the specified panel.
34244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34246 hidePanel : function(panel){
34247 if(this.tabs && (panel = this.getPanel(panel))){
34248 this.tabs.hideTab(panel.getEl().id);
34253 * Unhides the tab for a previously hidden panel.
34254 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34256 unhidePanel : function(panel){
34257 if(this.tabs && (panel = this.getPanel(panel))){
34258 this.tabs.unhideTab(panel.getEl().id);
34262 clearPanels : function(){
34263 while(this.panels.getCount() > 0){
34264 this.remove(this.panels.first());
34269 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34270 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34271 * @param {Boolean} preservePanel Overrides the config preservePanel option
34272 * @return {Roo.ContentPanel} The panel that was removed
34274 remove : function(panel, preservePanel)
34276 panel = this.getPanel(panel);
34281 this.fireEvent("beforeremove", this, panel, e);
34282 if(e.cancel === true){
34285 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34286 var panelId = panel.getId();
34287 this.panels.removeKey(panelId);
34289 document.body.appendChild(panel.getEl().dom);
34292 this.tabs.removeTab(panel.getEl().id);
34293 }else if (!preservePanel){
34294 this.bodyEl.dom.removeChild(panel.getEl().dom);
34296 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34297 var p = this.panels.first();
34298 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34299 tempEl.appendChild(p.getEl().dom);
34300 this.bodyEl.update("");
34301 this.bodyEl.dom.appendChild(p.getEl().dom);
34303 this.updateTitle(p.getTitle());
34305 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34306 this.setActivePanel(p);
34308 panel.setRegion(null);
34309 if(this.activePanel == panel){
34310 this.activePanel = null;
34312 if(this.config.autoDestroy !== false && preservePanel !== true){
34313 try{panel.destroy();}catch(e){}
34315 this.fireEvent("panelremoved", this, panel);
34320 * Returns the TabPanel component used by this region
34321 * @return {Roo.TabPanel}
34323 getTabs : function(){
34327 createTool : function(parentEl, className){
34328 var btn = Roo.DomHelper.append(parentEl, {
34330 cls: "x-layout-tools-button",
34333 cls: "roo-layout-tools-button-inner " + className,
34337 btn.addClassOnOver("roo-layout-tools-button-over");
34342 * Ext JS Library 1.1.1
34343 * Copyright(c) 2006-2007, Ext JS, LLC.
34345 * Originally Released Under LGPL - original licence link has changed is not relivant.
34348 * <script type="text/javascript">
34354 * @class Roo.SplitLayoutRegion
34355 * @extends Roo.LayoutRegion
34356 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34358 Roo.bootstrap.layout.Split = function(config){
34359 this.cursor = config.cursor;
34360 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34363 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34365 splitTip : "Drag to resize.",
34366 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34367 useSplitTips : false,
34369 applyConfig : function(config){
34370 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34373 onRender : function(ctr,pos) {
34375 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34376 if(!this.config.split){
34381 var splitEl = Roo.DomHelper.append(ctr.dom, {
34383 id: this.el.id + "-split",
34384 cls: "roo-layout-split roo-layout-split-"+this.position,
34387 /** The SplitBar for this region
34388 * @type Roo.SplitBar */
34389 // does not exist yet...
34390 Roo.log([this.position, this.orientation]);
34392 this.split = new Roo.bootstrap.SplitBar({
34393 dragElement : splitEl,
34394 resizingElement: this.el,
34395 orientation : this.orientation
34398 this.split.on("moved", this.onSplitMove, this);
34399 this.split.useShim = this.config.useShim === true;
34400 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34401 if(this.useSplitTips){
34402 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34404 //if(config.collapsible){
34405 // this.split.el.on("dblclick", this.collapse, this);
34408 if(typeof this.config.minSize != "undefined"){
34409 this.split.minSize = this.config.minSize;
34411 if(typeof this.config.maxSize != "undefined"){
34412 this.split.maxSize = this.config.maxSize;
34414 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34415 this.hideSplitter();
34420 getHMaxSize : function(){
34421 var cmax = this.config.maxSize || 10000;
34422 var center = this.mgr.getRegion("center");
34423 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34426 getVMaxSize : function(){
34427 var cmax = this.config.maxSize || 10000;
34428 var center = this.mgr.getRegion("center");
34429 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34432 onSplitMove : function(split, newSize){
34433 this.fireEvent("resized", this, newSize);
34437 * Returns the {@link Roo.SplitBar} for this region.
34438 * @return {Roo.SplitBar}
34440 getSplitBar : function(){
34445 this.hideSplitter();
34446 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34449 hideSplitter : function(){
34451 this.split.el.setLocation(-2000,-2000);
34452 this.split.el.hide();
34458 this.split.el.show();
34460 Roo.bootstrap.layout.Split.superclass.show.call(this);
34463 beforeSlide: function(){
34464 if(Roo.isGecko){// firefox overflow auto bug workaround
34465 this.bodyEl.clip();
34467 this.tabs.bodyEl.clip();
34469 if(this.activePanel){
34470 this.activePanel.getEl().clip();
34472 if(this.activePanel.beforeSlide){
34473 this.activePanel.beforeSlide();
34479 afterSlide : function(){
34480 if(Roo.isGecko){// firefox overflow auto bug workaround
34481 this.bodyEl.unclip();
34483 this.tabs.bodyEl.unclip();
34485 if(this.activePanel){
34486 this.activePanel.getEl().unclip();
34487 if(this.activePanel.afterSlide){
34488 this.activePanel.afterSlide();
34494 initAutoHide : function(){
34495 if(this.autoHide !== false){
34496 if(!this.autoHideHd){
34497 var st = new Roo.util.DelayedTask(this.slideIn, this);
34498 this.autoHideHd = {
34499 "mouseout": function(e){
34500 if(!e.within(this.el, true)){
34504 "mouseover" : function(e){
34510 this.el.on(this.autoHideHd);
34514 clearAutoHide : function(){
34515 if(this.autoHide !== false){
34516 this.el.un("mouseout", this.autoHideHd.mouseout);
34517 this.el.un("mouseover", this.autoHideHd.mouseover);
34521 clearMonitor : function(){
34522 Roo.get(document).un("click", this.slideInIf, this);
34525 // these names are backwards but not changed for compat
34526 slideOut : function(){
34527 if(this.isSlid || this.el.hasActiveFx()){
34530 this.isSlid = true;
34531 if(this.collapseBtn){
34532 this.collapseBtn.hide();
34534 this.closeBtnState = this.closeBtn.getStyle('display');
34535 this.closeBtn.hide();
34537 this.stickBtn.show();
34540 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34541 this.beforeSlide();
34542 this.el.setStyle("z-index", 10001);
34543 this.el.slideIn(this.getSlideAnchor(), {
34544 callback: function(){
34546 this.initAutoHide();
34547 Roo.get(document).on("click", this.slideInIf, this);
34548 this.fireEvent("slideshow", this);
34555 afterSlideIn : function(){
34556 this.clearAutoHide();
34557 this.isSlid = false;
34558 this.clearMonitor();
34559 this.el.setStyle("z-index", "");
34560 if(this.collapseBtn){
34561 this.collapseBtn.show();
34563 this.closeBtn.setStyle('display', this.closeBtnState);
34565 this.stickBtn.hide();
34567 this.fireEvent("slidehide", this);
34570 slideIn : function(cb){
34571 if(!this.isSlid || this.el.hasActiveFx()){
34575 this.isSlid = false;
34576 this.beforeSlide();
34577 this.el.slideOut(this.getSlideAnchor(), {
34578 callback: function(){
34579 this.el.setLeftTop(-10000, -10000);
34581 this.afterSlideIn();
34589 slideInIf : function(e){
34590 if(!e.within(this.el)){
34595 animateCollapse : function(){
34596 this.beforeSlide();
34597 this.el.setStyle("z-index", 20000);
34598 var anchor = this.getSlideAnchor();
34599 this.el.slideOut(anchor, {
34600 callback : function(){
34601 this.el.setStyle("z-index", "");
34602 this.collapsedEl.slideIn(anchor, {duration:.3});
34604 this.el.setLocation(-10000,-10000);
34606 this.fireEvent("collapsed", this);
34613 animateExpand : function(){
34614 this.beforeSlide();
34615 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34616 this.el.setStyle("z-index", 20000);
34617 this.collapsedEl.hide({
34620 this.el.slideIn(this.getSlideAnchor(), {
34621 callback : function(){
34622 this.el.setStyle("z-index", "");
34625 this.split.el.show();
34627 this.fireEvent("invalidated", this);
34628 this.fireEvent("expanded", this);
34656 getAnchor : function(){
34657 return this.anchors[this.position];
34660 getCollapseAnchor : function(){
34661 return this.canchors[this.position];
34664 getSlideAnchor : function(){
34665 return this.sanchors[this.position];
34668 getAlignAdj : function(){
34669 var cm = this.cmargins;
34670 switch(this.position){
34686 getExpandAdj : function(){
34687 var c = this.collapsedEl, cm = this.cmargins;
34688 switch(this.position){
34690 return [-(cm.right+c.getWidth()+cm.left), 0];
34693 return [cm.right+c.getWidth()+cm.left, 0];
34696 return [0, -(cm.top+cm.bottom+c.getHeight())];
34699 return [0, cm.top+cm.bottom+c.getHeight()];
34705 * Ext JS Library 1.1.1
34706 * Copyright(c) 2006-2007, Ext JS, LLC.
34708 * Originally Released Under LGPL - original licence link has changed is not relivant.
34711 * <script type="text/javascript">
34714 * These classes are private internal classes
34716 Roo.bootstrap.layout.Center = function(config){
34717 config.region = "center";
34718 Roo.bootstrap.layout.Region.call(this, config);
34719 this.visible = true;
34720 this.minWidth = config.minWidth || 20;
34721 this.minHeight = config.minHeight || 20;
34724 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34726 // center panel can't be hidden
34730 // center panel can't be hidden
34733 getMinWidth: function(){
34734 return this.minWidth;
34737 getMinHeight: function(){
34738 return this.minHeight;
34751 Roo.bootstrap.layout.North = function(config)
34753 config.region = 'north';
34754 config.cursor = 'n-resize';
34756 Roo.bootstrap.layout.Split.call(this, config);
34760 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34761 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34762 this.split.el.addClass("roo-layout-split-v");
34764 var size = config.initialSize || config.height;
34765 if(typeof size != "undefined"){
34766 this.el.setHeight(size);
34769 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34771 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34775 getBox : function(){
34776 if(this.collapsed){
34777 return this.collapsedEl.getBox();
34779 var box = this.el.getBox();
34781 box.height += this.split.el.getHeight();
34786 updateBox : function(box){
34787 if(this.split && !this.collapsed){
34788 box.height -= this.split.el.getHeight();
34789 this.split.el.setLeft(box.x);
34790 this.split.el.setTop(box.y+box.height);
34791 this.split.el.setWidth(box.width);
34793 if(this.collapsed){
34794 this.updateBody(box.width, null);
34796 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34804 Roo.bootstrap.layout.South = function(config){
34805 config.region = 'south';
34806 config.cursor = 's-resize';
34807 Roo.bootstrap.layout.Split.call(this, config);
34809 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34810 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34811 this.split.el.addClass("roo-layout-split-v");
34813 var size = config.initialSize || config.height;
34814 if(typeof size != "undefined"){
34815 this.el.setHeight(size);
34819 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34820 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34821 getBox : function(){
34822 if(this.collapsed){
34823 return this.collapsedEl.getBox();
34825 var box = this.el.getBox();
34827 var sh = this.split.el.getHeight();
34834 updateBox : function(box){
34835 if(this.split && !this.collapsed){
34836 var sh = this.split.el.getHeight();
34839 this.split.el.setLeft(box.x);
34840 this.split.el.setTop(box.y-sh);
34841 this.split.el.setWidth(box.width);
34843 if(this.collapsed){
34844 this.updateBody(box.width, null);
34846 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34850 Roo.bootstrap.layout.East = function(config){
34851 config.region = "east";
34852 config.cursor = "e-resize";
34853 Roo.bootstrap.layout.Split.call(this, config);
34855 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34856 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34857 this.split.el.addClass("roo-layout-split-h");
34859 var size = config.initialSize || config.width;
34860 if(typeof size != "undefined"){
34861 this.el.setWidth(size);
34864 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34865 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34866 getBox : function(){
34867 if(this.collapsed){
34868 return this.collapsedEl.getBox();
34870 var box = this.el.getBox();
34872 var sw = this.split.el.getWidth();
34879 updateBox : function(box){
34880 if(this.split && !this.collapsed){
34881 var sw = this.split.el.getWidth();
34883 this.split.el.setLeft(box.x);
34884 this.split.el.setTop(box.y);
34885 this.split.el.setHeight(box.height);
34888 if(this.collapsed){
34889 this.updateBody(null, box.height);
34891 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34895 Roo.bootstrap.layout.West = function(config){
34896 config.region = "west";
34897 config.cursor = "w-resize";
34899 Roo.bootstrap.layout.Split.call(this, config);
34901 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34902 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34903 this.split.el.addClass("roo-layout-split-h");
34907 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34908 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34910 onRender: function(ctr, pos)
34912 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34913 var size = this.config.initialSize || this.config.width;
34914 if(typeof size != "undefined"){
34915 this.el.setWidth(size);
34919 getBox : function(){
34920 if(this.collapsed){
34921 return this.collapsedEl.getBox();
34923 var box = this.el.getBox();
34925 box.width += this.split.el.getWidth();
34930 updateBox : function(box){
34931 if(this.split && !this.collapsed){
34932 var sw = this.split.el.getWidth();
34934 this.split.el.setLeft(box.x+box.width);
34935 this.split.el.setTop(box.y);
34936 this.split.el.setHeight(box.height);
34938 if(this.collapsed){
34939 this.updateBody(null, box.height);
34941 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34944 Roo.namespace("Roo.bootstrap.panel");/*
34946 * Ext JS Library 1.1.1
34947 * Copyright(c) 2006-2007, Ext JS, LLC.
34949 * Originally Released Under LGPL - original licence link has changed is not relivant.
34952 * <script type="text/javascript">
34955 * @class Roo.ContentPanel
34956 * @extends Roo.util.Observable
34957 * A basic ContentPanel element.
34958 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34959 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34960 * @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
34961 * @cfg {Boolean} closable True if the panel can be closed/removed
34962 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34963 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34964 * @cfg {Toolbar} toolbar A toolbar for this panel
34965 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34966 * @cfg {String} title The title for this panel
34967 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34968 * @cfg {String} url Calls {@link #setUrl} with this value
34969 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34970 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34971 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34972 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34973 * @cfg {Boolean} badges render the badges
34976 * Create a new ContentPanel.
34977 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34978 * @param {String/Object} config A string to set only the title or a config object
34979 * @param {String} content (optional) Set the HTML content for this panel
34980 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34982 Roo.bootstrap.panel.Content = function( config){
34984 this.tpl = config.tpl || false;
34986 var el = config.el;
34987 var content = config.content;
34989 if(config.autoCreate){ // xtype is available if this is called from factory
34992 this.el = Roo.get(el);
34993 if(!this.el && config && config.autoCreate){
34994 if(typeof config.autoCreate == "object"){
34995 if(!config.autoCreate.id){
34996 config.autoCreate.id = config.id||el;
34998 this.el = Roo.DomHelper.append(document.body,
34999 config.autoCreate, true);
35001 var elcfg = { tag: "div",
35002 cls: "roo-layout-inactive-content",
35006 elcfg.html = config.html;
35010 this.el = Roo.DomHelper.append(document.body, elcfg , true);
35013 this.closable = false;
35014 this.loaded = false;
35015 this.active = false;
35018 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35020 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35022 this.wrapEl = this.el; //this.el.wrap();
35024 if (config.toolbar.items) {
35025 ti = config.toolbar.items ;
35026 delete config.toolbar.items ;
35030 this.toolbar.render(this.wrapEl, 'before');
35031 for(var i =0;i < ti.length;i++) {
35032 // Roo.log(['add child', items[i]]);
35033 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35035 this.toolbar.items = nitems;
35036 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35037 delete config.toolbar;
35041 // xtype created footer. - not sure if will work as we normally have to render first..
35042 if (this.footer && !this.footer.el && this.footer.xtype) {
35043 if (!this.wrapEl) {
35044 this.wrapEl = this.el.wrap();
35047 this.footer.container = this.wrapEl.createChild();
35049 this.footer = Roo.factory(this.footer, Roo);
35054 if(typeof config == "string"){
35055 this.title = config;
35057 Roo.apply(this, config);
35061 this.resizeEl = Roo.get(this.resizeEl, true);
35063 this.resizeEl = this.el;
35065 // handle view.xtype
35073 * Fires when this panel is activated.
35074 * @param {Roo.ContentPanel} this
35078 * @event deactivate
35079 * Fires when this panel is activated.
35080 * @param {Roo.ContentPanel} this
35082 "deactivate" : true,
35086 * Fires when this panel is resized if fitToFrame is true.
35087 * @param {Roo.ContentPanel} this
35088 * @param {Number} width The width after any component adjustments
35089 * @param {Number} height The height after any component adjustments
35095 * Fires when this tab is created
35096 * @param {Roo.ContentPanel} this
35107 if(this.autoScroll){
35108 this.resizeEl.setStyle("overflow", "auto");
35110 // fix randome scrolling
35111 //this.el.on('scroll', function() {
35112 // Roo.log('fix random scolling');
35113 // this.scrollTo('top',0);
35116 content = content || this.content;
35118 this.setContent(content);
35120 if(config && config.url){
35121 this.setUrl(this.url, this.params, this.loadOnce);
35126 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35128 if (this.view && typeof(this.view.xtype) != 'undefined') {
35129 this.view.el = this.el.appendChild(document.createElement("div"));
35130 this.view = Roo.factory(this.view);
35131 this.view.render && this.view.render(false, '');
35135 this.fireEvent('render', this);
35138 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35142 setRegion : function(region){
35143 this.region = region;
35144 this.setActiveClass(region && !this.background);
35148 setActiveClass: function(state)
35151 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35152 this.el.setStyle('position','relative');
35154 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35155 this.el.setStyle('position', 'absolute');
35160 * Returns the toolbar for this Panel if one was configured.
35161 * @return {Roo.Toolbar}
35163 getToolbar : function(){
35164 return this.toolbar;
35167 setActiveState : function(active)
35169 this.active = active;
35170 this.setActiveClass(active);
35172 this.fireEvent("deactivate", this);
35174 this.fireEvent("activate", this);
35178 * Updates this panel's element
35179 * @param {String} content The new content
35180 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35182 setContent : function(content, loadScripts){
35183 this.el.update(content, loadScripts);
35186 ignoreResize : function(w, h){
35187 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35190 this.lastSize = {width: w, height: h};
35195 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35196 * @return {Roo.UpdateManager} The UpdateManager
35198 getUpdateManager : function(){
35199 return this.el.getUpdateManager();
35202 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35203 * @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:
35206 url: "your-url.php",
35207 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35208 callback: yourFunction,
35209 scope: yourObject, //(optional scope)
35212 text: "Loading...",
35217 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35218 * 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.
35219 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
35220 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35221 * @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.
35222 * @return {Roo.ContentPanel} this
35225 var um = this.el.getUpdateManager();
35226 um.update.apply(um, arguments);
35232 * 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.
35233 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35234 * @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)
35235 * @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)
35236 * @return {Roo.UpdateManager} The UpdateManager
35238 setUrl : function(url, params, loadOnce){
35239 if(this.refreshDelegate){
35240 this.removeListener("activate", this.refreshDelegate);
35242 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35243 this.on("activate", this.refreshDelegate);
35244 return this.el.getUpdateManager();
35247 _handleRefresh : function(url, params, loadOnce){
35248 if(!loadOnce || !this.loaded){
35249 var updater = this.el.getUpdateManager();
35250 updater.update(url, params, this._setLoaded.createDelegate(this));
35254 _setLoaded : function(){
35255 this.loaded = true;
35259 * Returns this panel's id
35262 getId : function(){
35267 * Returns this panel's element - used by regiosn to add.
35268 * @return {Roo.Element}
35270 getEl : function(){
35271 return this.wrapEl || this.el;
35276 adjustForComponents : function(width, height)
35278 //Roo.log('adjustForComponents ');
35279 if(this.resizeEl != this.el){
35280 width -= this.el.getFrameWidth('lr');
35281 height -= this.el.getFrameWidth('tb');
35284 var te = this.toolbar.getEl();
35285 height -= te.getHeight();
35286 te.setWidth(width);
35289 var te = this.footer.getEl();
35290 Roo.log("footer:" + te.getHeight());
35292 height -= te.getHeight();
35293 te.setWidth(width);
35297 if(this.adjustments){
35298 width += this.adjustments[0];
35299 height += this.adjustments[1];
35301 return {"width": width, "height": height};
35304 setSize : function(width, height){
35305 if(this.fitToFrame && !this.ignoreResize(width, height)){
35306 if(this.fitContainer && this.resizeEl != this.el){
35307 this.el.setSize(width, height);
35309 var size = this.adjustForComponents(width, height);
35310 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35311 this.fireEvent('resize', this, size.width, size.height);
35316 * Returns this panel's title
35319 getTitle : function(){
35324 * Set this panel's title
35325 * @param {String} title
35327 setTitle : function(title){
35328 this.title = title;
35330 this.region.updatePanelTitle(this, title);
35335 * Returns true is this panel was configured to be closable
35336 * @return {Boolean}
35338 isClosable : function(){
35339 return this.closable;
35342 beforeSlide : function(){
35344 this.resizeEl.clip();
35347 afterSlide : function(){
35349 this.resizeEl.unclip();
35353 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35354 * Will fail silently if the {@link #setUrl} method has not been called.
35355 * This does not activate the panel, just updates its content.
35357 refresh : function(){
35358 if(this.refreshDelegate){
35359 this.loaded = false;
35360 this.refreshDelegate();
35365 * Destroys this panel
35367 destroy : function(){
35368 this.el.removeAllListeners();
35369 var tempEl = document.createElement("span");
35370 tempEl.appendChild(this.el.dom);
35371 tempEl.innerHTML = "";
35377 * form - if the content panel contains a form - this is a reference to it.
35378 * @type {Roo.form.Form}
35382 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35383 * This contains a reference to it.
35389 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35399 * @param {Object} cfg Xtype definition of item to add.
35403 getChildContainer: function () {
35404 return this.getEl();
35409 var ret = new Roo.factory(cfg);
35414 if (cfg.xtype.match(/^Form$/)) {
35417 //if (this.footer) {
35418 // el = this.footer.container.insertSibling(false, 'before');
35420 el = this.el.createChild();
35423 this.form = new Roo.form.Form(cfg);
35426 if ( this.form.allItems.length) {
35427 this.form.render(el.dom);
35431 // should only have one of theses..
35432 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35433 // views.. should not be just added - used named prop 'view''
35435 cfg.el = this.el.appendChild(document.createElement("div"));
35438 var ret = new Roo.factory(cfg);
35440 ret.render && ret.render(false, ''); // render blank..
35450 * @class Roo.bootstrap.panel.Grid
35451 * @extends Roo.bootstrap.panel.Content
35453 * Create a new GridPanel.
35454 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35455 * @param {Object} config A the config object
35461 Roo.bootstrap.panel.Grid = function(config)
35465 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35466 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35468 config.el = this.wrapper;
35469 //this.el = this.wrapper;
35471 if (config.container) {
35472 // ctor'ed from a Border/panel.grid
35475 this.wrapper.setStyle("overflow", "hidden");
35476 this.wrapper.addClass('roo-grid-container');
35481 if(config.toolbar){
35482 var tool_el = this.wrapper.createChild();
35483 this.toolbar = Roo.factory(config.toolbar);
35485 if (config.toolbar.items) {
35486 ti = config.toolbar.items ;
35487 delete config.toolbar.items ;
35491 this.toolbar.render(tool_el);
35492 for(var i =0;i < ti.length;i++) {
35493 // Roo.log(['add child', items[i]]);
35494 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35496 this.toolbar.items = nitems;
35498 delete config.toolbar;
35501 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35502 config.grid.scrollBody = true;;
35503 config.grid.monitorWindowResize = false; // turn off autosizing
35504 config.grid.autoHeight = false;
35505 config.grid.autoWidth = false;
35507 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35509 if (config.background) {
35510 // render grid on panel activation (if panel background)
35511 this.on('activate', function(gp) {
35512 if (!gp.grid.rendered) {
35513 gp.grid.render(this.wrapper);
35514 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35519 this.grid.render(this.wrapper);
35520 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35523 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35524 // ??? needed ??? config.el = this.wrapper;
35529 // xtype created footer. - not sure if will work as we normally have to render first..
35530 if (this.footer && !this.footer.el && this.footer.xtype) {
35532 var ctr = this.grid.getView().getFooterPanel(true);
35533 this.footer.dataSource = this.grid.dataSource;
35534 this.footer = Roo.factory(this.footer, Roo);
35535 this.footer.render(ctr);
35545 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35546 getId : function(){
35547 return this.grid.id;
35551 * Returns the grid for this panel
35552 * @return {Roo.bootstrap.Table}
35554 getGrid : function(){
35558 setSize : function(width, height){
35559 if(!this.ignoreResize(width, height)){
35560 var grid = this.grid;
35561 var size = this.adjustForComponents(width, height);
35562 var gridel = grid.getGridEl();
35563 gridel.setSize(size.width, size.height);
35565 var thd = grid.getGridEl().select('thead',true).first();
35566 var tbd = grid.getGridEl().select('tbody', true).first();
35568 tbd.setSize(width, height - thd.getHeight());
35577 beforeSlide : function(){
35578 this.grid.getView().scroller.clip();
35581 afterSlide : function(){
35582 this.grid.getView().scroller.unclip();
35585 destroy : function(){
35586 this.grid.destroy();
35588 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35593 * @class Roo.bootstrap.panel.Nest
35594 * @extends Roo.bootstrap.panel.Content
35596 * Create a new Panel, that can contain a layout.Border.
35599 * @param {Roo.BorderLayout} layout The layout for this panel
35600 * @param {String/Object} config A string to set only the title or a config object
35602 Roo.bootstrap.panel.Nest = function(config)
35604 // construct with only one argument..
35605 /* FIXME - implement nicer consturctors
35606 if (layout.layout) {
35608 layout = config.layout;
35609 delete config.layout;
35611 if (layout.xtype && !layout.getEl) {
35612 // then layout needs constructing..
35613 layout = Roo.factory(layout, Roo);
35617 config.el = config.layout.getEl();
35619 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35621 config.layout.monitorWindowResize = false; // turn off autosizing
35622 this.layout = config.layout;
35623 this.layout.getEl().addClass("roo-layout-nested-layout");
35630 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35632 setSize : function(width, height){
35633 if(!this.ignoreResize(width, height)){
35634 var size = this.adjustForComponents(width, height);
35635 var el = this.layout.getEl();
35636 if (size.height < 1) {
35637 el.setWidth(size.width);
35639 el.setSize(size.width, size.height);
35641 var touch = el.dom.offsetWidth;
35642 this.layout.layout();
35643 // ie requires a double layout on the first pass
35644 if(Roo.isIE && !this.initialized){
35645 this.initialized = true;
35646 this.layout.layout();
35651 // activate all subpanels if not currently active..
35653 setActiveState : function(active){
35654 this.active = active;
35655 this.setActiveClass(active);
35658 this.fireEvent("deactivate", this);
35662 this.fireEvent("activate", this);
35663 // not sure if this should happen before or after..
35664 if (!this.layout) {
35665 return; // should not happen..
35668 for (var r in this.layout.regions) {
35669 reg = this.layout.getRegion(r);
35670 if (reg.getActivePanel()) {
35671 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35672 reg.setActivePanel(reg.getActivePanel());
35675 if (!reg.panels.length) {
35678 reg.showPanel(reg.getPanel(0));
35687 * Returns the nested BorderLayout for this panel
35688 * @return {Roo.BorderLayout}
35690 getLayout : function(){
35691 return this.layout;
35695 * Adds a xtype elements to the layout of the nested panel
35699 xtype : 'ContentPanel',
35706 xtype : 'NestedLayoutPanel',
35712 items : [ ... list of content panels or nested layout panels.. ]
35716 * @param {Object} cfg Xtype definition of item to add.
35718 addxtype : function(cfg) {
35719 return this.layout.addxtype(cfg);
35724 * Ext JS Library 1.1.1
35725 * Copyright(c) 2006-2007, Ext JS, LLC.
35727 * Originally Released Under LGPL - original licence link has changed is not relivant.
35730 * <script type="text/javascript">
35733 * @class Roo.TabPanel
35734 * @extends Roo.util.Observable
35735 * A lightweight tab container.
35739 // basic tabs 1, built from existing content
35740 var tabs = new Roo.TabPanel("tabs1");
35741 tabs.addTab("script", "View Script");
35742 tabs.addTab("markup", "View Markup");
35743 tabs.activate("script");
35745 // more advanced tabs, built from javascript
35746 var jtabs = new Roo.TabPanel("jtabs");
35747 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35749 // set up the UpdateManager
35750 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35751 var updater = tab2.getUpdateManager();
35752 updater.setDefaultUrl("ajax1.htm");
35753 tab2.on('activate', updater.refresh, updater, true);
35755 // Use setUrl for Ajax loading
35756 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35757 tab3.setUrl("ajax2.htm", null, true);
35760 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35763 jtabs.activate("jtabs-1");
35766 * Create a new TabPanel.
35767 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35768 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35770 Roo.bootstrap.panel.Tabs = function(config){
35772 * The container element for this TabPanel.
35773 * @type Roo.Element
35775 this.el = Roo.get(config.el);
35778 if(typeof config == "boolean"){
35779 this.tabPosition = config ? "bottom" : "top";
35781 Roo.apply(this, config);
35785 if(this.tabPosition == "bottom"){
35786 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35787 this.el.addClass("roo-tabs-bottom");
35789 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35790 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35791 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35793 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35795 if(this.tabPosition != "bottom"){
35796 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35797 * @type Roo.Element
35799 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35800 this.el.addClass("roo-tabs-top");
35804 this.bodyEl.setStyle("position", "relative");
35806 this.active = null;
35807 this.activateDelegate = this.activate.createDelegate(this);
35812 * Fires when the active tab changes
35813 * @param {Roo.TabPanel} this
35814 * @param {Roo.TabPanelItem} activePanel The new active tab
35818 * @event beforetabchange
35819 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35820 * @param {Roo.TabPanel} this
35821 * @param {Object} e Set cancel to true on this object to cancel the tab change
35822 * @param {Roo.TabPanelItem} tab The tab being changed to
35824 "beforetabchange" : true
35827 Roo.EventManager.onWindowResize(this.onResize, this);
35828 this.cpad = this.el.getPadding("lr");
35829 this.hiddenCount = 0;
35832 // toolbar on the tabbar support...
35833 if (this.toolbar) {
35834 alert("no toolbar support yet");
35835 this.toolbar = false;
35837 var tcfg = this.toolbar;
35838 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35839 this.toolbar = new Roo.Toolbar(tcfg);
35840 if (Roo.isSafari) {
35841 var tbl = tcfg.container.child('table', true);
35842 tbl.setAttribute('width', '100%');
35850 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35853 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35855 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35857 tabPosition : "top",
35859 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35861 currentTabWidth : 0,
35863 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35867 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35871 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35873 preferredTabWidth : 175,
35875 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35877 resizeTabs : false,
35879 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35881 monitorResize : true,
35883 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35888 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35889 * @param {String} id The id of the div to use <b>or create</b>
35890 * @param {String} text The text for the tab
35891 * @param {String} content (optional) Content to put in the TabPanelItem body
35892 * @param {Boolean} closable (optional) True to create a close icon on the tab
35893 * @return {Roo.TabPanelItem} The created TabPanelItem
35895 addTab : function(id, text, content, closable, tpl)
35897 var item = new Roo.bootstrap.panel.TabItem({
35901 closable : closable,
35904 this.addTabItem(item);
35906 item.setContent(content);
35912 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35913 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35914 * @return {Roo.TabPanelItem}
35916 getTab : function(id){
35917 return this.items[id];
35921 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35922 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35924 hideTab : function(id){
35925 var t = this.items[id];
35928 this.hiddenCount++;
35929 this.autoSizeTabs();
35934 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35935 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35937 unhideTab : function(id){
35938 var t = this.items[id];
35940 t.setHidden(false);
35941 this.hiddenCount--;
35942 this.autoSizeTabs();
35947 * Adds an existing {@link Roo.TabPanelItem}.
35948 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35950 addTabItem : function(item){
35951 this.items[item.id] = item;
35952 this.items.push(item);
35953 // if(this.resizeTabs){
35954 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35955 // this.autoSizeTabs();
35957 // item.autoSize();
35962 * Removes a {@link Roo.TabPanelItem}.
35963 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35965 removeTab : function(id){
35966 var items = this.items;
35967 var tab = items[id];
35968 if(!tab) { return; }
35969 var index = items.indexOf(tab);
35970 if(this.active == tab && items.length > 1){
35971 var newTab = this.getNextAvailable(index);
35976 this.stripEl.dom.removeChild(tab.pnode.dom);
35977 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35978 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35980 items.splice(index, 1);
35981 delete this.items[tab.id];
35982 tab.fireEvent("close", tab);
35983 tab.purgeListeners();
35984 this.autoSizeTabs();
35987 getNextAvailable : function(start){
35988 var items = this.items;
35990 // look for a next tab that will slide over to
35991 // replace the one being removed
35992 while(index < items.length){
35993 var item = items[++index];
35994 if(item && !item.isHidden()){
35998 // if one isn't found select the previous tab (on the left)
36001 var item = items[--index];
36002 if(item && !item.isHidden()){
36010 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36011 * @param {String/Number} id The id or index of the TabPanelItem to disable.
36013 disableTab : function(id){
36014 var tab = this.items[id];
36015 if(tab && this.active != tab){
36021 * Enables a {@link Roo.TabPanelItem} that is disabled.
36022 * @param {String/Number} id The id or index of the TabPanelItem to enable.
36024 enableTab : function(id){
36025 var tab = this.items[id];
36030 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36031 * @param {String/Number} id The id or index of the TabPanelItem to activate.
36032 * @return {Roo.TabPanelItem} The TabPanelItem.
36034 activate : function(id){
36035 var tab = this.items[id];
36039 if(tab == this.active || tab.disabled){
36043 this.fireEvent("beforetabchange", this, e, tab);
36044 if(e.cancel !== true && !tab.disabled){
36046 this.active.hide();
36048 this.active = this.items[id];
36049 this.active.show();
36050 this.fireEvent("tabchange", this, this.active);
36056 * Gets the active {@link Roo.TabPanelItem}.
36057 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36059 getActiveTab : function(){
36060 return this.active;
36064 * Updates the tab body element to fit the height of the container element
36065 * for overflow scrolling
36066 * @param {Number} targetHeight (optional) Override the starting height from the elements height
36068 syncHeight : function(targetHeight){
36069 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36070 var bm = this.bodyEl.getMargins();
36071 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36072 this.bodyEl.setHeight(newHeight);
36076 onResize : function(){
36077 if(this.monitorResize){
36078 this.autoSizeTabs();
36083 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36085 beginUpdate : function(){
36086 this.updating = true;
36090 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36092 endUpdate : function(){
36093 this.updating = false;
36094 this.autoSizeTabs();
36098 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36100 autoSizeTabs : function(){
36101 var count = this.items.length;
36102 var vcount = count - this.hiddenCount;
36103 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36106 var w = Math.max(this.el.getWidth() - this.cpad, 10);
36107 var availWidth = Math.floor(w / vcount);
36108 var b = this.stripBody;
36109 if(b.getWidth() > w){
36110 var tabs = this.items;
36111 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36112 if(availWidth < this.minTabWidth){
36113 /*if(!this.sleft){ // incomplete scrolling code
36114 this.createScrollButtons();
36117 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36120 if(this.currentTabWidth < this.preferredTabWidth){
36121 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36127 * Returns the number of tabs in this TabPanel.
36130 getCount : function(){
36131 return this.items.length;
36135 * Resizes all the tabs to the passed width
36136 * @param {Number} The new width
36138 setTabWidth : function(width){
36139 this.currentTabWidth = width;
36140 for(var i = 0, len = this.items.length; i < len; i++) {
36141 if(!this.items[i].isHidden()) {
36142 this.items[i].setWidth(width);
36148 * Destroys this TabPanel
36149 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36151 destroy : function(removeEl){
36152 Roo.EventManager.removeResizeListener(this.onResize, this);
36153 for(var i = 0, len = this.items.length; i < len; i++){
36154 this.items[i].purgeListeners();
36156 if(removeEl === true){
36157 this.el.update("");
36162 createStrip : function(container)
36164 var strip = document.createElement("nav");
36165 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36166 container.appendChild(strip);
36170 createStripList : function(strip)
36172 // div wrapper for retard IE
36173 // returns the "tr" element.
36174 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36175 //'<div class="x-tabs-strip-wrap">'+
36176 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36177 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36178 return strip.firstChild; //.firstChild.firstChild.firstChild;
36180 createBody : function(container)
36182 var body = document.createElement("div");
36183 Roo.id(body, "tab-body");
36184 //Roo.fly(body).addClass("x-tabs-body");
36185 Roo.fly(body).addClass("tab-content");
36186 container.appendChild(body);
36189 createItemBody :function(bodyEl, id){
36190 var body = Roo.getDom(id);
36192 body = document.createElement("div");
36195 //Roo.fly(body).addClass("x-tabs-item-body");
36196 Roo.fly(body).addClass("tab-pane");
36197 bodyEl.insertBefore(body, bodyEl.firstChild);
36201 createStripElements : function(stripEl, text, closable, tpl)
36203 var td = document.createElement("li"); // was td..
36206 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36209 stripEl.appendChild(td);
36211 td.className = "x-tabs-closable";
36212 if(!this.closeTpl){
36213 this.closeTpl = new Roo.Template(
36214 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36215 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36216 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36219 var el = this.closeTpl.overwrite(td, {"text": text});
36220 var close = el.getElementsByTagName("div")[0];
36221 var inner = el.getElementsByTagName("em")[0];
36222 return {"el": el, "close": close, "inner": inner};
36225 // not sure what this is..
36226 // if(!this.tabTpl){
36227 //this.tabTpl = new Roo.Template(
36228 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36229 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36231 // this.tabTpl = new Roo.Template(
36232 // '<a href="#">' +
36233 // '<span unselectable="on"' +
36234 // (this.disableTooltips ? '' : ' title="{text}"') +
36235 // ' >{text}</span></a>'
36241 var template = tpl || this.tabTpl || false;
36245 template = new Roo.Template(
36247 '<span unselectable="on"' +
36248 (this.disableTooltips ? '' : ' title="{text}"') +
36249 ' >{text}</span></a>'
36253 switch (typeof(template)) {
36257 template = new Roo.Template(template);
36263 var el = template.overwrite(td, {"text": text});
36265 var inner = el.getElementsByTagName("span")[0];
36267 return {"el": el, "inner": inner};
36275 * @class Roo.TabPanelItem
36276 * @extends Roo.util.Observable
36277 * Represents an individual item (tab plus body) in a TabPanel.
36278 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36279 * @param {String} id The id of this TabPanelItem
36280 * @param {String} text The text for the tab of this TabPanelItem
36281 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36283 Roo.bootstrap.panel.TabItem = function(config){
36285 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36286 * @type Roo.TabPanel
36288 this.tabPanel = config.panel;
36290 * The id for this TabPanelItem
36293 this.id = config.id;
36295 this.disabled = false;
36297 this.text = config.text;
36299 this.loaded = false;
36300 this.closable = config.closable;
36303 * The body element for this TabPanelItem.
36304 * @type Roo.Element
36306 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36307 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36308 this.bodyEl.setStyle("display", "block");
36309 this.bodyEl.setStyle("zoom", "1");
36310 //this.hideAction();
36312 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36314 this.el = Roo.get(els.el);
36315 this.inner = Roo.get(els.inner, true);
36316 this.textEl = Roo.get(this.el.dom.firstChild, true);
36317 this.pnode = Roo.get(els.el.parentNode, true);
36318 this.el.on("mousedown", this.onTabMouseDown, this);
36319 this.el.on("click", this.onTabClick, this);
36321 if(config.closable){
36322 var c = Roo.get(els.close, true);
36323 c.dom.title = this.closeText;
36324 c.addClassOnOver("close-over");
36325 c.on("click", this.closeClick, this);
36331 * Fires when this tab becomes the active tab.
36332 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36333 * @param {Roo.TabPanelItem} this
36337 * @event beforeclose
36338 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36339 * @param {Roo.TabPanelItem} this
36340 * @param {Object} e Set cancel to true on this object to cancel the close.
36342 "beforeclose": true,
36345 * Fires when this tab is closed.
36346 * @param {Roo.TabPanelItem} this
36350 * @event deactivate
36351 * Fires when this tab is no longer the active tab.
36352 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36353 * @param {Roo.TabPanelItem} this
36355 "deactivate" : true
36357 this.hidden = false;
36359 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36362 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36364 purgeListeners : function(){
36365 Roo.util.Observable.prototype.purgeListeners.call(this);
36366 this.el.removeAllListeners();
36369 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36372 this.pnode.addClass("active");
36375 this.tabPanel.stripWrap.repaint();
36377 this.fireEvent("activate", this.tabPanel, this);
36381 * Returns true if this tab is the active tab.
36382 * @return {Boolean}
36384 isActive : function(){
36385 return this.tabPanel.getActiveTab() == this;
36389 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36392 this.pnode.removeClass("active");
36394 this.fireEvent("deactivate", this.tabPanel, this);
36397 hideAction : function(){
36398 this.bodyEl.hide();
36399 this.bodyEl.setStyle("position", "absolute");
36400 this.bodyEl.setLeft("-20000px");
36401 this.bodyEl.setTop("-20000px");
36404 showAction : function(){
36405 this.bodyEl.setStyle("position", "relative");
36406 this.bodyEl.setTop("");
36407 this.bodyEl.setLeft("");
36408 this.bodyEl.show();
36412 * Set the tooltip for the tab.
36413 * @param {String} tooltip The tab's tooltip
36415 setTooltip : function(text){
36416 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36417 this.textEl.dom.qtip = text;
36418 this.textEl.dom.removeAttribute('title');
36420 this.textEl.dom.title = text;
36424 onTabClick : function(e){
36425 e.preventDefault();
36426 this.tabPanel.activate(this.id);
36429 onTabMouseDown : function(e){
36430 e.preventDefault();
36431 this.tabPanel.activate(this.id);
36434 getWidth : function(){
36435 return this.inner.getWidth();
36438 setWidth : function(width){
36439 var iwidth = width - this.pnode.getPadding("lr");
36440 this.inner.setWidth(iwidth);
36441 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36442 this.pnode.setWidth(width);
36446 * Show or hide the tab
36447 * @param {Boolean} hidden True to hide or false to show.
36449 setHidden : function(hidden){
36450 this.hidden = hidden;
36451 this.pnode.setStyle("display", hidden ? "none" : "");
36455 * Returns true if this tab is "hidden"
36456 * @return {Boolean}
36458 isHidden : function(){
36459 return this.hidden;
36463 * Returns the text for this tab
36466 getText : function(){
36470 autoSize : function(){
36471 //this.el.beginMeasure();
36472 this.textEl.setWidth(1);
36474 * #2804 [new] Tabs in Roojs
36475 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36477 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36478 //this.el.endMeasure();
36482 * Sets the text for the tab (Note: this also sets the tooltip text)
36483 * @param {String} text The tab's text and tooltip
36485 setText : function(text){
36487 this.textEl.update(text);
36488 this.setTooltip(text);
36489 //if(!this.tabPanel.resizeTabs){
36490 // this.autoSize();
36494 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36496 activate : function(){
36497 this.tabPanel.activate(this.id);
36501 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36503 disable : function(){
36504 if(this.tabPanel.active != this){
36505 this.disabled = true;
36506 this.pnode.addClass("disabled");
36511 * Enables this TabPanelItem if it was previously disabled.
36513 enable : function(){
36514 this.disabled = false;
36515 this.pnode.removeClass("disabled");
36519 * Sets the content for this TabPanelItem.
36520 * @param {String} content The content
36521 * @param {Boolean} loadScripts true to look for and load scripts
36523 setContent : function(content, loadScripts){
36524 this.bodyEl.update(content, loadScripts);
36528 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36529 * @return {Roo.UpdateManager} The UpdateManager
36531 getUpdateManager : function(){
36532 return this.bodyEl.getUpdateManager();
36536 * Set a URL to be used to load the content for this TabPanelItem.
36537 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36538 * @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)
36539 * @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)
36540 * @return {Roo.UpdateManager} The UpdateManager
36542 setUrl : function(url, params, loadOnce){
36543 if(this.refreshDelegate){
36544 this.un('activate', this.refreshDelegate);
36546 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36547 this.on("activate", this.refreshDelegate);
36548 return this.bodyEl.getUpdateManager();
36552 _handleRefresh : function(url, params, loadOnce){
36553 if(!loadOnce || !this.loaded){
36554 var updater = this.bodyEl.getUpdateManager();
36555 updater.update(url, params, this._setLoaded.createDelegate(this));
36560 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36561 * Will fail silently if the setUrl method has not been called.
36562 * This does not activate the panel, just updates its content.
36564 refresh : function(){
36565 if(this.refreshDelegate){
36566 this.loaded = false;
36567 this.refreshDelegate();
36572 _setLoaded : function(){
36573 this.loaded = true;
36577 closeClick : function(e){
36580 this.fireEvent("beforeclose", this, o);
36581 if(o.cancel !== true){
36582 this.tabPanel.removeTab(this.id);
36586 * The text displayed in the tooltip for the close icon.
36589 closeText : "Close this tab"