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);
233 addxtypeChild : function (tree, cntr, is_body)
235 Roo.debug && Roo.log('addxtypeChild:' + cntr);
237 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241 (typeof(tree['flexy:foreach']) != 'undefined');
245 skip_children = false;
246 // render the element if it's not BODY.
249 cn = Roo.factory(tree);
251 cn.parentType = this.xtype; //??
252 cn.parentId = this.id;
254 var build_from_html = Roo.XComponent.build_from_html;
257 // does the container contain child eleemnts with 'xtype' attributes.
258 // that match this xtype..
259 // note - when we render we create these as well..
260 // so we should check to see if body has xtype set.
261 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263 var self_cntr_el = Roo.get(this[cntr](false));
264 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266 //Roo.log(Roo.XComponent.build_from_html);
267 //Roo.log("got echild:");
270 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271 // and are not displayed -this causes this to use up the wrong element when matching.
272 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
275 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
282 //echild.dom.removeAttribute('xtype');
284 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285 Roo.debug && Roo.log(self_cntr_el);
286 Roo.debug && Roo.log(echild);
287 Roo.debug && Roo.log(cn);
293 // if object has flexy:if - then it may or may not be rendered.
294 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
295 // skip a flexy if element.
296 Roo.debug && Roo.log('skipping render');
297 Roo.debug && Roo.log(tree);
299 Roo.debug && Roo.log('skipping all children');
300 skip_children = true;
305 // actually if flexy:foreach is found, we really want to create
306 // multiple copies here...
308 //Roo.log(this[cntr]());
309 // some elements do not have render methods.. like the layouts...
310 cn.render && cn.render(this[cntr](true));
312 // then add the element..
320 if (typeof (tree.menu) != 'undefined') {
321 tree.menu.parentType = cn.xtype;
322 tree.menu.triggerEl = cn.el;
323 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
327 if (!tree.items || !tree.items.length) {
329 //Roo.log(["no children", this]);
334 var items = tree.items;
337 //Roo.log(items.length);
339 if (!skip_children) {
340 for(var i =0;i < items.length;i++) {
341 // Roo.log(['add child', items[i]]);
342 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
348 //Roo.log("fire childrenrendered");
350 cn.fireEvent('childrenrendered', this);
355 * Show a component - removes 'hidden' class
360 this.el.removeClass('hidden');
364 * Hide a component - adds 'hidden' class
368 if (this.el && !this.el.hasClass('hidden')) {
369 this.el.addClass('hidden');
383 * @class Roo.bootstrap.Body
384 * @extends Roo.bootstrap.Component
385 * Bootstrap Body class
389 * @param {Object} config The config object
392 Roo.bootstrap.Body = function(config){
393 Roo.bootstrap.Body.superclass.constructor.call(this, config);
394 this.el = Roo.get(document.body);
395 if (this.cls && this.cls.length) {
396 Roo.get(document.body).addClass(this.cls);
400 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
402 is_body : true,// just to make sure it's constructed?
407 onRender : function(ct, position)
409 /* Roo.log("Roo.bootstrap.Body - onRender");
410 if (this.cls && this.cls.length) {
411 Roo.get(document.body).addClass(this.cls);
431 * @class Roo.bootstrap.ButtonGroup
432 * @extends Roo.bootstrap.Component
433 * Bootstrap ButtonGroup class
434 * @cfg {String} size lg | sm | xs (default empty normal)
435 * @cfg {String} align vertical | justified (default none)
436 * @cfg {String} direction up | down (default down)
437 * @cfg {Boolean} toolbar false | true
438 * @cfg {Boolean} btn true | false
443 * @param {Object} config The config object
446 Roo.bootstrap.ButtonGroup = function(config){
447 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
458 getAutoCreate : function(){
464 cfg.html = this.html || cfg.html;
475 if (['vertical','justified'].indexOf(this.align)!==-1) {
476 cfg.cls = 'btn-group-' + this.align;
478 if (this.align == 'justified') {
479 console.log(this.items);
483 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
484 cfg.cls += ' btn-group-' + this.size;
487 if (this.direction == 'up') {
488 cfg.cls += ' dropup' ;
504 * @class Roo.bootstrap.Button
505 * @extends Roo.bootstrap.Component
506 * Bootstrap Button class
507 * @cfg {String} html The button content
508 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
509 * @cfg {String} size ( lg | sm | xs)
510 * @cfg {String} tag ( a | input | submit)
511 * @cfg {String} href empty or href
512 * @cfg {Boolean} disabled default false;
513 * @cfg {Boolean} isClose default false;
514 * @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)
515 * @cfg {String} badge text for badge
516 * @cfg {String} theme default
517 * @cfg {Boolean} inverse
518 * @cfg {Boolean} toggle
519 * @cfg {String} ontext text for on toggle state
520 * @cfg {String} offtext text for off toggle state
521 * @cfg {Boolean} defaulton
522 * @cfg {Boolean} preventDefault default true
523 * @cfg {Boolean} removeClass remove the standard class..
524 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
527 * Create a new button
528 * @param {Object} config The config object
532 Roo.bootstrap.Button = function(config){
533 Roo.bootstrap.Button.superclass.constructor.call(this, config);
538 * When a butotn is pressed
539 * @param {Roo.bootstrap.Button} this
540 * @param {Roo.EventObject} e
545 * After the button has been toggles
546 * @param {Roo.EventObject} e
547 * @param {boolean} pressed (also available as button.pressed)
553 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
571 preventDefault: true,
580 getAutoCreate : function(){
588 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
589 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
594 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596 if (this.toggle == true) {
599 cls: 'slider-frame roo-button',
604 'data-off-text':'OFF',
605 cls: 'slider-button',
611 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
612 cfg.cls += ' '+this.weight;
621 cfg["aria-hidden"] = true;
623 cfg.html = "×";
629 if (this.theme==='default') {
630 cfg.cls = 'btn roo-button';
632 //if (this.parentType != 'Navbar') {
633 this.weight = this.weight.length ? this.weight : 'default';
635 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637 cfg.cls += ' btn-' + this.weight;
639 } else if (this.theme==='glow') {
642 cfg.cls = 'btn-glow roo-button';
644 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646 cfg.cls += ' ' + this.weight;
652 this.cls += ' inverse';
657 cfg.cls += ' active';
661 cfg.disabled = 'disabled';
665 Roo.log('changing to ul' );
667 this.glyphicon = 'caret';
670 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672 //gsRoo.log(this.parentType);
673 if (this.parentType === 'Navbar' && !this.parent().bar) {
674 Roo.log('changing to li?');
683 href : this.href || '#'
686 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
687 cfg.cls += ' dropdown';
694 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
696 if (this.glyphicon) {
697 cfg.html = ' ' + cfg.html;
702 cls: 'glyphicon glyphicon-' + this.glyphicon
712 // cfg.cls='btn roo-button';
716 var value = cfg.html;
721 cls: 'glyphicon glyphicon-' + this.glyphicon,
740 cfg.cls += ' dropdown';
741 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
744 if (cfg.tag !== 'a' && this.href !== '') {
745 throw "Tag must be a to set href.";
746 } else if (this.href.length > 0) {
747 cfg.href = this.href;
750 if(this.removeClass){
755 cfg.target = this.target;
760 initEvents: function() {
761 // Roo.log('init events?');
762 // Roo.log(this.el.dom);
765 if (typeof (this.menu) != 'undefined') {
766 this.menu.parentType = this.xtype;
767 this.menu.triggerEl = this.el;
768 this.addxtype(Roo.apply({}, this.menu));
772 if (this.el.hasClass('roo-button')) {
773 this.el.on('click', this.onClick, this);
775 this.el.select('.roo-button').on('click', this.onClick, this);
778 if(this.removeClass){
779 this.el.on('click', this.onClick, this);
782 this.el.enableDisplayMode();
785 onClick : function(e)
792 Roo.log('button on click ');
793 if(this.preventDefault){
796 if (this.pressed === true || this.pressed === false) {
797 this.pressed = !this.pressed;
798 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
799 this.fireEvent('toggle', this, e, this.pressed);
803 this.fireEvent('click', this, e);
807 * Enables this button
811 this.disabled = false;
812 this.el.removeClass('disabled');
816 * Disable this button
820 this.disabled = true;
821 this.el.addClass('disabled');
824 * sets the active state on/off,
825 * @param {Boolean} state (optional) Force a particular state
827 setActive : function(v) {
829 this.el[v ? 'addClass' : 'removeClass']('active');
832 * toggles the current active state
834 toggleActive : function()
836 var active = this.el.hasClass('active');
837 this.setActive(!active);
841 setText : function(str)
843 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870 * @class Roo.bootstrap.Column
871 * @extends Roo.bootstrap.Component
872 * Bootstrap Column class
873 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
874 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
875 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
876 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
877 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
878 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
879 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
880 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
883 * @cfg {Boolean} hidden (true|false) hide the element
884 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
885 * @cfg {String} fa (ban|check|...) font awesome icon
886 * @cfg {Number} fasize (1|2|....) font awsome size
888 * @cfg {String} icon (info-sign|check|...) glyphicon name
890 * @cfg {String} html content of column.
893 * Create a new Column
894 * @param {Object} config The config object
897 Roo.bootstrap.Column = function(config){
898 Roo.bootstrap.Column.superclass.constructor.call(this, config);
901 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
919 getAutoCreate : function(){
920 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
928 ['xs','sm','md','lg'].map(function(size){
929 //Roo.log( size + ':' + settings[size]);
931 if (settings[size+'off'] !== false) {
932 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
935 if (settings[size] === false) {
939 if (!settings[size]) { // 0 = hidden
940 cfg.cls += ' hidden-' + size;
943 cfg.cls += ' col-' + size + '-' + settings[size];
948 cfg.cls += ' hidden';
951 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
952 cfg.cls +=' alert alert-' + this.alert;
956 if (this.html.length) {
957 cfg.html = this.html;
961 if (this.fasize > 1) {
962 fasize = ' fa-' + this.fasize + 'x';
964 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
969 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
988 * @class Roo.bootstrap.Container
989 * @extends Roo.bootstrap.Component
990 * Bootstrap Container class
991 * @cfg {Boolean} jumbotron is it a jumbotron element
992 * @cfg {String} html content of element
993 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
994 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
995 * @cfg {String} header content of header (for panel)
996 * @cfg {String} footer content of footer (for panel)
997 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
998 * @cfg {String} tag (header|aside|section) type of HTML tag.
999 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1000 * @cfg {String} fa font awesome icon
1001 * @cfg {String} icon (info-sign|check|...) glyphicon name
1002 * @cfg {Boolean} hidden (true|false) hide the element
1003 * @cfg {Boolean} expandable (true|false) default false
1004 * @cfg {Boolean} expanded (true|false) default true
1005 * @cfg {String} rheader contet on the right of header
1006 * @cfg {Boolean} clickable (true|false) default false
1010 * Create a new Container
1011 * @param {Object} config The config object
1014 Roo.bootstrap.Container = function(config){
1015 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1021 * After the panel has been expand
1023 * @param {Roo.bootstrap.Container} this
1028 * After the panel has been collapsed
1030 * @param {Roo.bootstrap.Container} this
1035 * When a element is chick
1036 * @param {Roo.bootstrap.Container} this
1037 * @param {Roo.EventObject} e
1043 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1061 getChildContainer : function() {
1067 if (this.panel.length) {
1068 return this.el.select('.panel-body',true).first();
1075 getAutoCreate : function(){
1078 tag : this.tag || 'div',
1082 if (this.jumbotron) {
1083 cfg.cls = 'jumbotron';
1088 // - this is applied by the parent..
1090 // cfg.cls = this.cls + '';
1093 if (this.sticky.length) {
1095 var bd = Roo.get(document.body);
1096 if (!bd.hasClass('bootstrap-sticky')) {
1097 bd.addClass('bootstrap-sticky');
1098 Roo.select('html',true).setStyle('height', '100%');
1101 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105 if (this.well.length) {
1106 switch (this.well) {
1109 cfg.cls +=' well well-' +this.well;
1118 cfg.cls += ' hidden';
1122 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1123 cfg.cls +=' alert alert-' + this.alert;
1128 if (this.panel.length) {
1129 cfg.cls += ' panel panel-' + this.panel;
1131 if (this.header.length) {
1135 if(this.expandable){
1137 cfg.cls = cfg.cls + ' expandable';
1141 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1149 cls : 'panel-title',
1150 html : (this.expandable ? ' ' : '') + this.header
1154 cls: 'panel-header-right',
1160 cls : 'panel-heading',
1161 style : this.expandable ? 'cursor: pointer' : '',
1169 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1174 if (this.footer.length) {
1176 cls : 'panel-footer',
1185 body.html = this.html || cfg.html;
1186 // prefix with the icons..
1188 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1191 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1196 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1197 cfg.cls = 'container';
1203 initEvents: function()
1205 if(this.expandable){
1206 var headerEl = this.headerEl();
1209 headerEl.on('click', this.onToggleClick, this);
1214 this.el.on('click', this.onClick, this);
1219 onToggleClick : function()
1221 var headerEl = this.headerEl();
1237 if(this.fireEvent('expand', this)) {
1239 this.expanded = true;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243 this.el.select('.panel-body',true).first().removeClass('hide');
1245 var toggleEl = this.toggleEl();
1251 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1256 collapse : function()
1258 if(this.fireEvent('collapse', this)) {
1260 this.expanded = false;
1262 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1263 this.el.select('.panel-body',true).first().addClass('hide');
1265 var toggleEl = this.toggleEl();
1271 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275 toggleEl : function()
1277 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281 return this.el.select('.panel-heading .fa',true).first();
1284 headerEl : function()
1286 if(!this.el || !this.panel.length || !this.header.length){
1290 return this.el.select('.panel-heading',true).first()
1293 titleEl : function()
1295 if(!this.el || !this.panel.length || !this.header.length){
1299 return this.el.select('.panel-title',true).first();
1302 setTitle : function(v)
1304 var titleEl = this.titleEl();
1310 titleEl.dom.innerHTML = v;
1313 getTitle : function()
1316 var titleEl = this.titleEl();
1322 return titleEl.dom.innerHTML;
1325 setRightTitle : function(v)
1327 var t = this.el.select('.panel-header-right',true).first();
1333 t.dom.innerHTML = v;
1336 onClick : function(e)
1340 this.fireEvent('click', this, e);
1354 * @class Roo.bootstrap.Img
1355 * @extends Roo.bootstrap.Component
1356 * Bootstrap Img class
1357 * @cfg {Boolean} imgResponsive false | true
1358 * @cfg {String} border rounded | circle | thumbnail
1359 * @cfg {String} src image source
1360 * @cfg {String} alt image alternative text
1361 * @cfg {String} href a tag href
1362 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1363 * @cfg {String} xsUrl xs image source
1364 * @cfg {String} smUrl sm image source
1365 * @cfg {String} mdUrl md image source
1366 * @cfg {String} lgUrl lg image source
1369 * Create a new Input
1370 * @param {Object} config The config object
1373 Roo.bootstrap.Img = function(config){
1374 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1380 * The img click event for the img.
1381 * @param {Roo.EventObject} e
1387 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1389 imgResponsive: true,
1399 getAutoCreate : function()
1401 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1402 return this.createSingleImg();
1407 cls: 'roo-image-responsive-group',
1412 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414 if(!_this[size + 'Url']){
1420 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1421 html: _this.html || cfg.html,
1422 src: _this[size + 'Url']
1425 img.cls += ' roo-image-responsive-' + size;
1427 var s = ['xs', 'sm', 'md', 'lg'];
1429 s.splice(s.indexOf(size), 1);
1431 Roo.each(s, function(ss){
1432 img.cls += ' hidden-' + ss;
1435 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1436 cfg.cls += ' img-' + _this.border;
1440 cfg.alt = _this.alt;
1453 a.target = _this.target;
1457 cfg.cn.push((_this.href) ? a : img);
1464 createSingleImg : function()
1468 cls: (this.imgResponsive) ? 'img-responsive' : '',
1470 src : 'about:blank' // just incase src get's set to undefined?!?
1473 cfg.html = this.html || cfg.html;
1475 cfg.src = this.src || cfg.src;
1477 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1478 cfg.cls += ' img-' + this.border;
1495 a.target = this.target;
1500 return (this.href) ? a : cfg;
1503 initEvents: function()
1506 this.el.on('click', this.onClick, this);
1511 onClick : function(e)
1513 Roo.log('img onclick');
1514 this.fireEvent('click', this, e);
1528 * @class Roo.bootstrap.Link
1529 * @extends Roo.bootstrap.Component
1530 * Bootstrap Link Class
1531 * @cfg {String} alt image alternative text
1532 * @cfg {String} href a tag href
1533 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1534 * @cfg {String} html the content of the link.
1535 * @cfg {String} anchor name for the anchor link
1536 * @cfg {String} fa - favicon
1538 * @cfg {Boolean} preventDefault (true | false) default false
1542 * Create a new Input
1543 * @param {Object} config The config object
1546 Roo.bootstrap.Link = function(config){
1547 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1553 * The img click event for the img.
1554 * @param {Roo.EventObject} e
1560 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1564 preventDefault: false,
1570 getAutoCreate : function()
1572 var html = this.html || '';
1574 if (this.fa !== false) {
1575 html = '<i class="fa fa-' + this.fa + '"></i>';
1580 // anchor's do not require html/href...
1581 if (this.anchor === false) {
1583 cfg.href = this.href || '#';
1585 cfg.name = this.anchor;
1586 if (this.html !== false || this.fa !== false) {
1589 if (this.href !== false) {
1590 cfg.href = this.href;
1594 if(this.alt !== false){
1599 if(this.target !== false) {
1600 cfg.target = this.target;
1606 initEvents: function() {
1608 if(!this.href || this.preventDefault){
1609 this.el.on('click', this.onClick, this);
1613 onClick : function(e)
1615 if(this.preventDefault){
1618 //Roo.log('img onclick');
1619 this.fireEvent('click', this, e);
1632 * @class Roo.bootstrap.Header
1633 * @extends Roo.bootstrap.Component
1634 * Bootstrap Header class
1635 * @cfg {String} html content of header
1636 * @cfg {Number} level (1|2|3|4|5|6) default 1
1639 * Create a new Header
1640 * @param {Object} config The config object
1644 Roo.bootstrap.Header = function(config){
1645 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1648 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1656 getAutoCreate : function(){
1661 tag: 'h' + (1 *this.level),
1662 html: this.html || ''
1674 * Ext JS Library 1.1.1
1675 * Copyright(c) 2006-2007, Ext JS, LLC.
1677 * Originally Released Under LGPL - original licence link has changed is not relivant.
1680 * <script type="text/javascript">
1684 * @class Roo.bootstrap.MenuMgr
1685 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1688 Roo.bootstrap.MenuMgr = function(){
1689 var menus, active, groups = {}, attached = false, lastShow = new Date();
1691 // private - called when first menu is created
1694 active = new Roo.util.MixedCollection();
1695 Roo.get(document).addKeyListener(27, function(){
1696 if(active.length > 0){
1704 if(active && active.length > 0){
1705 var c = active.clone();
1715 if(active.length < 1){
1716 Roo.get(document).un("mouseup", onMouseDown);
1724 var last = active.last();
1725 lastShow = new Date();
1728 Roo.get(document).on("mouseup", onMouseDown);
1733 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1734 m.parentMenu.activeChild = m;
1735 }else if(last && last.isVisible()){
1736 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1741 function onBeforeHide(m){
1743 m.activeChild.hide();
1745 if(m.autoHideTimer){
1746 clearTimeout(m.autoHideTimer);
1747 delete m.autoHideTimer;
1752 function onBeforeShow(m){
1753 var pm = m.parentMenu;
1754 if(!pm && !m.allowOtherMenus){
1756 }else if(pm && pm.activeChild && active != m){
1757 pm.activeChild.hide();
1761 // private this should really trigger on mouseup..
1762 function onMouseDown(e){
1763 Roo.log("on Mouse Up");
1765 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1766 Roo.log("MenuManager hideAll");
1775 function onBeforeCheck(mi, state){
1777 var g = groups[mi.group];
1778 for(var i = 0, l = g.length; i < l; i++){
1780 g[i].setChecked(false);
1789 * Hides all menus that are currently visible
1791 hideAll : function(){
1796 register : function(menu){
1800 menus[menu.id] = menu;
1801 menu.on("beforehide", onBeforeHide);
1802 menu.on("hide", onHide);
1803 menu.on("beforeshow", onBeforeShow);
1804 menu.on("show", onShow);
1806 if(g && menu.events["checkchange"]){
1810 groups[g].push(menu);
1811 menu.on("checkchange", onCheck);
1816 * Returns a {@link Roo.menu.Menu} object
1817 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1818 * be used to generate and return a new Menu instance.
1820 get : function(menu){
1821 if(typeof menu == "string"){ // menu id
1823 }else if(menu.events){ // menu instance
1826 /*else if(typeof menu.length == 'number'){ // array of menu items?
1827 return new Roo.bootstrap.Menu({items:menu});
1828 }else{ // otherwise, must be a config
1829 return new Roo.bootstrap.Menu(menu);
1836 unregister : function(menu){
1837 delete menus[menu.id];
1838 menu.un("beforehide", onBeforeHide);
1839 menu.un("hide", onHide);
1840 menu.un("beforeshow", onBeforeShow);
1841 menu.un("show", onShow);
1843 if(g && menu.events["checkchange"]){
1844 groups[g].remove(menu);
1845 menu.un("checkchange", onCheck);
1850 registerCheckable : function(menuItem){
1851 var g = menuItem.group;
1856 groups[g].push(menuItem);
1857 menuItem.on("beforecheckchange", onBeforeCheck);
1862 unregisterCheckable : function(menuItem){
1863 var g = menuItem.group;
1865 groups[g].remove(menuItem);
1866 menuItem.un("beforecheckchange", onBeforeCheck);
1878 * @class Roo.bootstrap.Menu
1879 * @extends Roo.bootstrap.Component
1880 * Bootstrap Menu class - container for MenuItems
1881 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1882 * @cfg {bool} hidden if the menu should be hidden when rendered.
1883 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1884 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1888 * @param {Object} config The config object
1892 Roo.bootstrap.Menu = function(config){
1893 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1894 if (this.registerMenu && this.type != 'treeview') {
1895 Roo.bootstrap.MenuMgr.register(this);
1900 * Fires before this menu is displayed
1901 * @param {Roo.menu.Menu} this
1906 * Fires before this menu is hidden
1907 * @param {Roo.menu.Menu} this
1912 * Fires after this menu is displayed
1913 * @param {Roo.menu.Menu} this
1918 * Fires after this menu is hidden
1919 * @param {Roo.menu.Menu} this
1924 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1925 * @param {Roo.menu.Menu} this
1926 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1927 * @param {Roo.EventObject} e
1932 * Fires when the mouse is hovering over this menu
1933 * @param {Roo.menu.Menu} this
1934 * @param {Roo.EventObject} e
1935 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940 * Fires when the mouse exits this menu
1941 * @param {Roo.menu.Menu} this
1942 * @param {Roo.EventObject} e
1943 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * Fires when a menu item contained in this menu is clicked
1949 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1950 * @param {Roo.EventObject} e
1954 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1957 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1961 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1964 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966 registerMenu : true,
1968 menuItems :false, // stores the menu items..
1978 getChildContainer : function() {
1982 getAutoCreate : function(){
1984 //if (['right'].indexOf(this.align)!==-1) {
1985 // cfg.cn[1].cls += ' pull-right'
1991 cls : 'dropdown-menu' ,
1992 style : 'z-index:1000'
1996 if (this.type === 'submenu') {
1997 cfg.cls = 'submenu active';
1999 if (this.type === 'treeview') {
2000 cfg.cls = 'treeview-menu';
2005 initEvents : function() {
2007 // Roo.log("ADD event");
2008 // Roo.log(this.triggerEl.dom);
2010 this.triggerEl.on('click', this.onTriggerClick, this);
2012 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014 this.triggerEl.addClass('dropdown-toggle');
2017 this.el.on('touchstart' , this.onTouch, this);
2019 this.el.on('click' , this.onClick, this);
2021 this.el.on("mouseover", this.onMouseOver, this);
2022 this.el.on("mouseout", this.onMouseOut, this);
2026 findTargetItem : function(e)
2028 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2032 //Roo.log(t); Roo.log(t.id);
2034 //Roo.log(this.menuitems);
2035 return this.menuitems.get(t.id);
2037 //return this.items.get(t.menuItemId);
2043 onTouch : function(e)
2045 Roo.log("menu.onTouch");
2046 //e.stopEvent(); this make the user popdown broken
2050 onClick : function(e)
2052 Roo.log("menu.onClick");
2054 var t = this.findTargetItem(e);
2055 if(!t || t.isContainer){
2060 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2061 if(t == this.activeItem && t.shouldDeactivate(e)){
2062 this.activeItem.deactivate();
2063 delete this.activeItem;
2067 this.setActiveItem(t, true);
2075 Roo.log('pass click event');
2079 this.fireEvent("click", this, t, e);
2083 (function() { _this.hide(); }).defer(500);
2086 onMouseOver : function(e){
2087 var t = this.findTargetItem(e);
2090 // if(t.canActivate && !t.disabled){
2091 // this.setActiveItem(t, true);
2095 this.fireEvent("mouseover", this, e, t);
2097 isVisible : function(){
2098 return !this.hidden;
2100 onMouseOut : function(e){
2101 var t = this.findTargetItem(e);
2104 // if(t == this.activeItem && t.shouldDeactivate(e)){
2105 // this.activeItem.deactivate();
2106 // delete this.activeItem;
2109 this.fireEvent("mouseout", this, e, t);
2114 * Displays this menu relative to another element
2115 * @param {String/HTMLElement/Roo.Element} element The element to align to
2116 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2117 * the element (defaults to this.defaultAlign)
2118 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120 show : function(el, pos, parentMenu){
2121 this.parentMenu = parentMenu;
2125 this.fireEvent("beforeshow", this);
2126 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2129 * Displays this menu at a specific xy position
2130 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2131 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133 showAt : function(xy, parentMenu, /* private: */_e){
2134 this.parentMenu = parentMenu;
2139 this.fireEvent("beforeshow", this);
2140 //xy = this.el.adjustForConstraints(xy);
2144 this.hideMenuItems();
2145 this.hidden = false;
2146 this.triggerEl.addClass('open');
2148 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2149 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2152 if(this.el.getStyle('top').slice(-1) != "%"){
2157 this.fireEvent("show", this);
2163 this.doFocus.defer(50, this);
2167 doFocus : function(){
2169 this.focusEl.focus();
2174 * Hides this menu and optionally all parent menus
2175 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177 hide : function(deep)
2180 this.hideMenuItems();
2181 if(this.el && this.isVisible()){
2182 this.fireEvent("beforehide", this);
2183 if(this.activeItem){
2184 this.activeItem.deactivate();
2185 this.activeItem = null;
2187 this.triggerEl.removeClass('open');;
2189 this.fireEvent("hide", this);
2191 if(deep === true && this.parentMenu){
2192 this.parentMenu.hide(true);
2196 onTriggerClick : function(e)
2198 Roo.log('trigger click');
2200 var target = e.getTarget();
2202 Roo.log(target.nodeName.toLowerCase());
2204 if(target.nodeName.toLowerCase() === 'i'){
2210 onTriggerPress : function(e)
2212 Roo.log('trigger press');
2213 //Roo.log(e.getTarget());
2214 // Roo.log(this.triggerEl.dom);
2216 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2217 var pel = Roo.get(e.getTarget());
2218 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2219 Roo.log('is treeview or dropdown?');
2223 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2227 if (this.isVisible()) {
2232 this.show(this.triggerEl, false, false);
2235 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2242 hideMenuItems : function()
2244 Roo.log("hide Menu Items");
2248 //$(backdrop).remove()
2249 this.el.select('.open',true).each(function(aa) {
2251 aa.removeClass('open');
2252 //var parent = getParent($(this))
2253 //var relatedTarget = { relatedTarget: this }
2255 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2256 //if (e.isDefaultPrevented()) return
2257 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2260 addxtypeChild : function (tree, cntr) {
2261 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263 this.menuitems.add(comp);
2284 * @class Roo.bootstrap.MenuItem
2285 * @extends Roo.bootstrap.Component
2286 * Bootstrap MenuItem class
2287 * @cfg {String} html the menu label
2288 * @cfg {String} href the link
2289 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2290 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2291 * @cfg {Boolean} active used on sidebars to highlight active itesm
2292 * @cfg {String} fa favicon to show on left of menu item.
2293 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2297 * Create a new MenuItem
2298 * @param {Object} config The config object
2302 Roo.bootstrap.MenuItem = function(config){
2303 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2308 * The raw click event for the entire grid.
2309 * @param {Roo.bootstrap.MenuItem} this
2310 * @param {Roo.EventObject} e
2316 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2320 preventDefault: true,
2321 isContainer : false,
2325 getAutoCreate : function(){
2327 if(this.isContainer){
2330 cls: 'dropdown-menu-item'
2344 if (this.fa !== false) {
2347 cls : 'fa fa-' + this.fa
2356 cls: 'dropdown-menu-item',
2359 if (this.parent().type == 'treeview') {
2360 cfg.cls = 'treeview-menu';
2363 cfg.cls += ' active';
2368 anc.href = this.href || cfg.cn[0].href ;
2369 ctag.html = this.html || cfg.cn[0].html ;
2373 initEvents: function()
2375 if (this.parent().type == 'treeview') {
2376 this.el.select('a').on('click', this.onClick, this);
2379 this.menu.parentType = this.xtype;
2380 this.menu.triggerEl = this.el;
2381 this.menu = this.addxtype(Roo.apply({}, this.menu));
2385 onClick : function(e)
2387 Roo.log('item on click ');
2388 //if(this.preventDefault){
2389 // e.preventDefault();
2391 //this.parent().hideMenuItems();
2393 this.fireEvent('click', this, e);
2412 * @class Roo.bootstrap.MenuSeparator
2413 * @extends Roo.bootstrap.Component
2414 * Bootstrap MenuSeparator class
2417 * Create a new MenuItem
2418 * @param {Object} config The config object
2422 Roo.bootstrap.MenuSeparator = function(config){
2423 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2426 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2428 getAutoCreate : function(){
2447 * @class Roo.bootstrap.Modal
2448 * @extends Roo.bootstrap.Component
2449 * Bootstrap Modal class
2450 * @cfg {String} title Title of dialog
2451 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2452 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2453 * @cfg {Boolean} specificTitle default false
2454 * @cfg {Array} buttons Array of buttons or standard button set..
2455 * @cfg {String} buttonPosition (left|right|center) default right
2456 * @cfg {Boolean} animate default true
2457 * @cfg {Boolean} allow_close default true
2460 * Create a new Modal Dialog
2461 * @param {Object} config The config object
2464 Roo.bootstrap.Modal = function(config){
2465 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470 * The raw btnclick event for the button
2471 * @param {Roo.EventObject} e
2475 this.buttons = this.buttons || [];
2478 this.tmpl = Roo.factory(this.tmpl);
2483 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2485 title : 'test dialog',
2495 specificTitle: false,
2497 buttonPosition: 'right',
2511 onRender : function(ct, position)
2513 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2516 var cfg = Roo.apply({}, this.getAutoCreate());
2519 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2521 //if (!cfg.name.length) {
2525 cfg.cls += ' ' + this.cls;
2528 cfg.style = this.style;
2530 this.el = Roo.get(document.body).createChild(cfg, position);
2532 //var type = this.el.dom.type;
2535 if(this.tabIndex !== undefined){
2536 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2540 this.bodyEl = this.el.select('.modal-body',true).first();
2541 this.closeEl = this.el.select('.modal-header .close', true).first();
2542 this.footerEl = this.el.select('.modal-footer',true).first();
2543 this.titleEl = this.el.select('.modal-title',true).first();
2547 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2548 this.maskEl.enableDisplayMode("block");
2550 //this.el.addClass("x-dlg-modal");
2552 if (this.buttons.length) {
2553 Roo.each(this.buttons, function(bb) {
2554 var b = Roo.apply({}, bb);
2555 b.xns = b.xns || Roo.bootstrap;
2556 b.xtype = b.xtype || 'Button';
2557 if (typeof(b.listeners) == 'undefined') {
2558 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2561 var btn = Roo.factory(b);
2563 btn.render(this.el.select('.modal-footer div').first());
2567 // render the children.
2570 if(typeof(this.items) != 'undefined'){
2571 var items = this.items;
2574 for(var i =0;i < items.length;i++) {
2575 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2579 this.items = nitems;
2581 // where are these used - they used to be body/close/footer
2585 //this.el.addClass([this.fieldClass, this.cls]);
2589 getAutoCreate : function(){
2594 html : this.html || ''
2599 cls : 'modal-title',
2603 if(this.specificTitle){
2609 if (this.allow_close) {
2620 style : 'display: none',
2623 cls: "modal-dialog",
2626 cls : "modal-content",
2629 cls : 'modal-header',
2634 cls : 'modal-footer',
2638 cls: 'btn-' + this.buttonPosition
2655 modal.cls += ' fade';
2661 getChildContainer : function() {
2666 getButtonContainer : function() {
2667 return this.el.select('.modal-footer div',true).first();
2670 initEvents : function()
2672 if (this.allow_close) {
2673 this.closeEl.on('click', this.hide, this);
2678 window.addEventListener("resize", function() { _this.resize(); } );
2684 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2689 if (!this.rendered) {
2693 this.el.setStyle('display', 'block');
2695 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2698 this.el.addClass('in');
2701 this.el.addClass('in');
2705 // not sure how we can show data in here..
2707 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2710 Roo.get(document.body).addClass("x-body-masked");
2711 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2713 this.el.setStyle('zIndex', '10001');
2715 this.fireEvent('show', this);
2723 Roo.get(document.body).removeClass("x-body-masked");
2724 this.el.removeClass('in');
2725 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2727 if(this.animate){ // why
2729 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2731 this.el.setStyle('display', 'none');
2734 this.fireEvent('hide', this);
2737 addButton : function(str, cb)
2741 var b = Roo.apply({}, { html : str } );
2742 b.xns = b.xns || Roo.bootstrap;
2743 b.xtype = b.xtype || 'Button';
2744 if (typeof(b.listeners) == 'undefined') {
2745 b.listeners = { click : cb.createDelegate(this) };
2748 var btn = Roo.factory(b);
2750 btn.render(this.el.select('.modal-footer div').first());
2756 setDefaultButton : function(btn)
2758 //this.el.select('.modal-footer').()
2762 resizeTo: function(w,h)
2766 this.el.select('.modal-dialog',true).first().setWidth(w);
2767 if (this.diff === false) {
2768 this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2771 this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2775 setContentSize : function(w, h)
2779 onButtonClick: function(btn,e)
2782 this.fireEvent('btnclick', btn.name, e);
2785 * Set the title of the Dialog
2786 * @param {String} str new Title
2788 setTitle: function(str) {
2789 this.titleEl.dom.innerHTML = str;
2792 * Set the body of the Dialog
2793 * @param {String} str new Title
2795 setBody: function(str) {
2796 this.bodyEl.dom.innerHTML = str;
2799 * Set the body of the Dialog using the template
2800 * @param {Obj} data - apply this data to the template and replace the body contents.
2802 applyBody: function(obj)
2805 Roo.log("Error - using apply Body without a template");
2808 this.tmpl.overwrite(this.bodyEl, obj);
2814 Roo.apply(Roo.bootstrap.Modal, {
2816 * Button config that displays a single OK button
2825 * Button config that displays Yes and No buttons
2841 * Button config that displays OK and Cancel buttons
2856 * Button config that displays Yes, No and Cancel buttons
2879 * messagebox - can be used as a replace
2883 * @class Roo.MessageBox
2884 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2888 Roo.Msg.alert('Status', 'Changes saved successfully.');
2890 // Prompt for user data:
2891 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2893 // process text value...
2897 // Show a dialog using config options:
2899 title:'Save Changes?',
2900 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2901 buttons: Roo.Msg.YESNOCANCEL,
2908 Roo.bootstrap.MessageBox = function(){
2909 var dlg, opt, mask, waitTimer;
2910 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2911 var buttons, activeTextEl, bwidth;
2915 var handleButton = function(button){
2917 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2921 var handleHide = function(){
2923 dlg.el.removeClass(opt.cls);
2926 // Roo.TaskMgr.stop(waitTimer);
2927 // waitTimer = null;
2932 var updateButtons = function(b){
2935 buttons["ok"].hide();
2936 buttons["cancel"].hide();
2937 buttons["yes"].hide();
2938 buttons["no"].hide();
2939 //dlg.footer.dom.style.display = 'none';
2942 dlg.footerEl.dom.style.display = '';
2943 for(var k in buttons){
2944 if(typeof buttons[k] != "function"){
2947 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2948 width += buttons[k].el.getWidth()+15;
2958 var handleEsc = function(d, k, e){
2959 if(opt && opt.closable !== false){
2969 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2970 * @return {Roo.BasicDialog} The BasicDialog element
2972 getDialog : function(){
2974 dlg = new Roo.bootstrap.Modal( {
2977 //constraintoviewport:false,
2979 //collapsible : false,
2984 //buttonAlign:"center",
2985 closeClick : function(){
2986 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2989 handleButton("cancel");
2994 dlg.on("hide", handleHide);
2996 //dlg.addKeyListener(27, handleEsc);
2998 this.buttons = buttons;
2999 var bt = this.buttonText;
3000 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3001 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3002 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3003 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3005 bodyEl = dlg.bodyEl.createChild({
3007 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3008 '<textarea class="roo-mb-textarea"></textarea>' +
3009 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3011 msgEl = bodyEl.dom.firstChild;
3012 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3013 textboxEl.enableDisplayMode();
3014 textboxEl.addKeyListener([10,13], function(){
3015 if(dlg.isVisible() && opt && opt.buttons){
3018 }else if(opt.buttons.yes){
3019 handleButton("yes");
3023 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3024 textareaEl.enableDisplayMode();
3025 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3026 progressEl.enableDisplayMode();
3027 var pf = progressEl.dom.firstChild;
3029 pp = Roo.get(pf.firstChild);
3030 pp.setHeight(pf.offsetHeight);
3038 * Updates the message box body text
3039 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3040 * the XHTML-compliant non-breaking space character '&#160;')
3041 * @return {Roo.MessageBox} This message box
3043 updateText : function(text){
3044 if(!dlg.isVisible() && !opt.width){
3045 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3047 msgEl.innerHTML = text || ' ';
3049 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3050 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3052 Math.min(opt.width || cw , this.maxWidth),
3053 Math.max(opt.minWidth || this.minWidth, bwidth)
3056 activeTextEl.setWidth(w);
3058 if(dlg.isVisible()){
3059 dlg.fixedcenter = false;
3061 // to big, make it scroll. = But as usual stupid IE does not support
3064 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3065 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3066 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3068 bodyEl.dom.style.height = '';
3069 bodyEl.dom.style.overflowY = '';
3072 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3074 bodyEl.dom.style.overflowX = '';
3077 dlg.setContentSize(w, bodyEl.getHeight());
3078 if(dlg.isVisible()){
3079 dlg.fixedcenter = true;
3085 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3086 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3087 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3088 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3089 * @return {Roo.MessageBox} This message box
3091 updateProgress : function(value, text){
3093 this.updateText(text);
3095 if (pp) { // weird bug on my firefox - for some reason this is not defined
3096 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3102 * Returns true if the message box is currently displayed
3103 * @return {Boolean} True if the message box is visible, else false
3105 isVisible : function(){
3106 return dlg && dlg.isVisible();
3110 * Hides the message box if it is displayed
3113 if(this.isVisible()){
3119 * Displays a new message box, or reinitializes an existing message box, based on the config options
3120 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3121 * The following config object properties are supported:
3123 Property Type Description
3124 ---------- --------------- ------------------------------------------------------------------------------------
3125 animEl String/Element An id or Element from which the message box should animate as it opens and
3126 closes (defaults to undefined)
3127 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3128 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3129 closable Boolean False to hide the top-right close button (defaults to true). Note that
3130 progress and wait dialogs will ignore this property and always hide the
3131 close button as they can only be closed programmatically.
3132 cls String A custom CSS class to apply to the message box element
3133 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3134 displayed (defaults to 75)
3135 fn Function A callback function to execute after closing the dialog. The arguments to the
3136 function will be btn (the name of the button that was clicked, if applicable,
3137 e.g. "ok"), and text (the value of the active text field, if applicable).
3138 Progress and wait dialogs will ignore this option since they do not respond to
3139 user actions and can only be closed programmatically, so any required function
3140 should be called by the same code after it closes the dialog.
3141 icon String A CSS class that provides a background image to be used as an icon for
3142 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3143 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3144 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3145 modal Boolean False to allow user interaction with the page while the message box is
3146 displayed (defaults to true)
3147 msg String A string that will replace the existing message box body text (defaults
3148 to the XHTML-compliant non-breaking space character ' ')
3149 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3150 progress Boolean True to display a progress bar (defaults to false)
3151 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3152 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3153 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3154 title String The title text
3155 value String The string value to set into the active textbox element if displayed
3156 wait Boolean True to display a progress bar (defaults to false)
3157 width Number The width of the dialog in pixels
3164 msg: 'Please enter your address:',
3166 buttons: Roo.MessageBox.OKCANCEL,
3169 animEl: 'addAddressBtn'
3172 * @param {Object} config Configuration options
3173 * @return {Roo.MessageBox} This message box
3175 show : function(options)
3178 // this causes nightmares if you show one dialog after another
3179 // especially on callbacks..
3181 if(this.isVisible()){
3184 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3185 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3186 Roo.log("New Dialog Message:" + options.msg )
3187 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3188 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3191 var d = this.getDialog();
3193 d.setTitle(opt.title || " ");
3194 d.closeEl.setDisplayed(opt.closable !== false);
3195 activeTextEl = textboxEl;
3196 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3201 textareaEl.setHeight(typeof opt.multiline == "number" ?
3202 opt.multiline : this.defaultTextHeight);
3203 activeTextEl = textareaEl;
3212 progressEl.setDisplayed(opt.progress === true);
3213 this.updateProgress(0);
3214 activeTextEl.dom.value = opt.value || "";
3216 dlg.setDefaultButton(activeTextEl);
3218 var bs = opt.buttons;
3222 }else if(bs && bs.yes){
3223 db = buttons["yes"];
3225 dlg.setDefaultButton(db);
3227 bwidth = updateButtons(opt.buttons);
3228 this.updateText(opt.msg);
3230 d.el.addClass(opt.cls);
3232 d.proxyDrag = opt.proxyDrag === true;
3233 d.modal = opt.modal !== false;
3234 d.mask = opt.modal !== false ? mask : false;
3236 // force it to the end of the z-index stack so it gets a cursor in FF
3237 document.body.appendChild(dlg.el.dom);
3238 d.animateTarget = null;
3239 d.show(options.animEl);
3245 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3246 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3247 * and closing the message box when the process is complete.
3248 * @param {String} title The title bar text
3249 * @param {String} msg The message box body text
3250 * @return {Roo.MessageBox} This message box
3252 progress : function(title, msg){
3259 minWidth: this.minProgressWidth,
3266 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3267 * If a callback function is passed it will be called after the user clicks the button, and the
3268 * id of the button that was clicked will be passed as the only parameter to the callback
3269 * (could also be the top-right close button).
3270 * @param {String} title The title bar text
3271 * @param {String} msg The message box body text
3272 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3273 * @param {Object} scope (optional) The scope of the callback function
3274 * @return {Roo.MessageBox} This message box
3276 alert : function(title, msg, fn, scope){
3289 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3290 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3291 * You are responsible for closing the message box when the process is complete.
3292 * @param {String} msg The message box body text
3293 * @param {String} title (optional) The title bar text
3294 * @return {Roo.MessageBox} This message box
3296 wait : function(msg, title){
3307 waitTimer = Roo.TaskMgr.start({
3309 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3317 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3318 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3319 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3320 * @param {String} title The title bar text
3321 * @param {String} msg The message box body text
3322 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3323 * @param {Object} scope (optional) The scope of the callback function
3324 * @return {Roo.MessageBox} This message box
3326 confirm : function(title, msg, fn, scope){
3330 buttons: this.YESNO,
3339 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3340 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3341 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3342 * (could also be the top-right close button) and the text that was entered will be passed as the two
3343 * parameters to the callback.
3344 * @param {String} title The title bar text
3345 * @param {String} msg The message box body text
3346 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3347 * @param {Object} scope (optional) The scope of the callback function
3348 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3349 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3350 * @return {Roo.MessageBox} This message box
3352 prompt : function(title, msg, fn, scope, multiline){
3356 buttons: this.OKCANCEL,
3361 multiline: multiline,
3368 * Button config that displays a single OK button
3373 * Button config that displays Yes and No buttons
3376 YESNO : {yes:true, no:true},
3378 * Button config that displays OK and Cancel buttons
3381 OKCANCEL : {ok:true, cancel:true},
3383 * Button config that displays Yes, No and Cancel buttons
3386 YESNOCANCEL : {yes:true, no:true, cancel:true},
3389 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3392 defaultTextHeight : 75,
3394 * The maximum width in pixels of the message box (defaults to 600)
3399 * The minimum width in pixels of the message box (defaults to 100)
3404 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3405 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3408 minProgressWidth : 250,
3410 * An object containing the default button text strings that can be overriden for localized language support.
3411 * Supported properties are: ok, cancel, yes and no.
3412 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3425 * Shorthand for {@link Roo.MessageBox}
3427 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3428 Roo.Msg = Roo.Msg || Roo.MessageBox;
3437 * @class Roo.bootstrap.Navbar
3438 * @extends Roo.bootstrap.Component
3439 * Bootstrap Navbar class
3442 * Create a new Navbar
3443 * @param {Object} config The config object
3447 Roo.bootstrap.Navbar = function(config){
3448 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3452 * @event beforetoggle
3453 * Fire before toggle the menu
3454 * @param {Roo.EventObject} e
3456 "beforetoggle" : true
3460 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3469 getAutoCreate : function(){
3472 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3476 initEvents :function ()
3478 //Roo.log(this.el.select('.navbar-toggle',true));
3479 this.el.select('.navbar-toggle',true).on('click', function() {
3480 if(this.fireEvent('beforetoggle', this) !== false){
3481 this.el.select('.navbar-collapse',true).toggleClass('in');
3491 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3493 var size = this.el.getSize();
3494 this.maskEl.setSize(size.width, size.height);
3495 this.maskEl.enableDisplayMode("block");
3504 getChildContainer : function()
3506 if (this.el.select('.collapse').getCount()) {
3507 return this.el.select('.collapse',true).first();
3540 * @class Roo.bootstrap.NavSimplebar
3541 * @extends Roo.bootstrap.Navbar
3542 * Bootstrap Sidebar class
3544 * @cfg {Boolean} inverse is inverted color
3546 * @cfg {String} type (nav | pills | tabs)
3547 * @cfg {Boolean} arrangement stacked | justified
3548 * @cfg {String} align (left | right) alignment
3550 * @cfg {Boolean} main (true|false) main nav bar? default false
3551 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3553 * @cfg {String} tag (header|footer|nav|div) default is nav
3559 * Create a new Sidebar
3560 * @param {Object} config The config object
3564 Roo.bootstrap.NavSimplebar = function(config){
3565 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3568 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3584 getAutoCreate : function(){
3588 tag : this.tag || 'div',
3601 this.type = this.type || 'nav';
3602 if (['tabs','pills'].indexOf(this.type)!==-1) {
3603 cfg.cn[0].cls += ' nav-' + this.type
3607 if (this.type!=='nav') {
3608 Roo.log('nav type must be nav/tabs/pills')
3610 cfg.cn[0].cls += ' navbar-nav'
3616 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3617 cfg.cn[0].cls += ' nav-' + this.arrangement;
3621 if (this.align === 'right') {
3622 cfg.cn[0].cls += ' navbar-right';
3626 cfg.cls += ' navbar-inverse';
3653 * @class Roo.bootstrap.NavHeaderbar
3654 * @extends Roo.bootstrap.NavSimplebar
3655 * Bootstrap Sidebar class
3657 * @cfg {String} brand what is brand
3658 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3659 * @cfg {String} brand_href href of the brand
3660 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3661 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3662 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3663 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3666 * Create a new Sidebar
3667 * @param {Object} config The config object
3671 Roo.bootstrap.NavHeaderbar = function(config){
3672 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3676 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3683 desktopCenter : false,
3686 getAutoCreate : function(){
3689 tag: this.nav || 'nav',
3696 if (this.desktopCenter) {
3697 cn.push({cls : 'container', cn : []});
3704 cls: 'navbar-header',
3709 cls: 'navbar-toggle',
3710 'data-toggle': 'collapse',
3715 html: 'Toggle navigation'
3737 cls: 'collapse navbar-collapse',
3741 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3743 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3744 cfg.cls += ' navbar-' + this.position;
3746 // tag can override this..
3748 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3751 if (this.brand !== '') {
3754 href: this.brand_href ? this.brand_href : '#',
3755 cls: 'navbar-brand',
3763 cfg.cls += ' main-nav';
3771 getHeaderChildContainer : function()
3773 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3774 return this.el.select('.navbar-header',true).first();
3777 return this.getChildContainer();
3781 initEvents : function()
3783 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3785 if (this.autohide) {
3790 Roo.get(document).on('scroll',function(e) {
3791 var ns = Roo.get(document).getScroll().top;
3792 var os = prevScroll;
3796 ft.removeClass('slideDown');
3797 ft.addClass('slideUp');
3800 ft.removeClass('slideUp');
3801 ft.addClass('slideDown');
3822 * @class Roo.bootstrap.NavSidebar
3823 * @extends Roo.bootstrap.Navbar
3824 * Bootstrap Sidebar class
3827 * Create a new Sidebar
3828 * @param {Object} config The config object
3832 Roo.bootstrap.NavSidebar = function(config){
3833 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3836 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3838 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3840 getAutoCreate : function(){
3845 cls: 'sidebar sidebar-nav'
3867 * @class Roo.bootstrap.NavGroup
3868 * @extends Roo.bootstrap.Component
3869 * Bootstrap NavGroup class
3870 * @cfg {String} align (left|right)
3871 * @cfg {Boolean} inverse
3872 * @cfg {String} type (nav|pills|tab) default nav
3873 * @cfg {String} navId - reference Id for navbar.
3877 * Create a new nav group
3878 * @param {Object} config The config object
3881 Roo.bootstrap.NavGroup = function(config){
3882 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3885 Roo.bootstrap.NavGroup.register(this);
3889 * Fires when the active item changes
3890 * @param {Roo.bootstrap.NavGroup} this
3891 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3892 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3899 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3910 getAutoCreate : function()
3912 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3919 if (['tabs','pills'].indexOf(this.type)!==-1) {
3920 cfg.cls += ' nav-' + this.type
3922 if (this.type!=='nav') {
3923 Roo.log('nav type must be nav/tabs/pills')
3925 cfg.cls += ' navbar-nav'
3928 if (this.parent().sidebar) {
3931 cls: 'dashboard-menu sidebar-menu'
3937 if (this.form === true) {
3943 if (this.align === 'right') {
3944 cfg.cls += ' navbar-right';
3946 cfg.cls += ' navbar-left';
3950 if (this.align === 'right') {
3951 cfg.cls += ' navbar-right';
3955 cfg.cls += ' navbar-inverse';
3963 * sets the active Navigation item
3964 * @param {Roo.bootstrap.NavItem} the new current navitem
3966 setActiveItem : function(item)
3969 Roo.each(this.navItems, function(v){
3974 v.setActive(false, true);
3981 item.setActive(true, true);
3982 this.fireEvent('changed', this, item, prev);
3987 * gets the active Navigation item
3988 * @return {Roo.bootstrap.NavItem} the current navitem
3990 getActive : function()
3994 Roo.each(this.navItems, function(v){
4005 indexOfNav : function()
4009 Roo.each(this.navItems, function(v,i){
4020 * adds a Navigation item
4021 * @param {Roo.bootstrap.NavItem} the navitem to add
4023 addItem : function(cfg)
4025 var cn = new Roo.bootstrap.NavItem(cfg);
4027 cn.parentId = this.id;
4028 cn.onRender(this.el, null);
4032 * register a Navigation item
4033 * @param {Roo.bootstrap.NavItem} the navitem to add
4035 register : function(item)
4037 this.navItems.push( item);
4038 item.navId = this.navId;
4043 * clear all the Navigation item
4046 clearAll : function()
4049 this.el.dom.innerHTML = '';
4052 getNavItem: function(tabId)
4055 Roo.each(this.navItems, function(e) {
4056 if (e.tabId == tabId) {
4066 setActiveNext : function()
4068 var i = this.indexOfNav(this.getActive());
4069 if (i > this.navItems.length) {
4072 this.setActiveItem(this.navItems[i+1]);
4074 setActivePrev : function()
4076 var i = this.indexOfNav(this.getActive());
4080 this.setActiveItem(this.navItems[i-1]);
4082 clearWasActive : function(except) {
4083 Roo.each(this.navItems, function(e) {
4084 if (e.tabId != except.tabId && e.was_active) {
4085 e.was_active = false;
4092 getWasActive : function ()
4095 Roo.each(this.navItems, function(e) {
4110 Roo.apply(Roo.bootstrap.NavGroup, {
4114 * register a Navigation Group
4115 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4117 register : function(navgrp)
4119 this.groups[navgrp.navId] = navgrp;
4123 * fetch a Navigation Group based on the navigation ID
4124 * @param {string} the navgroup to add
4125 * @returns {Roo.bootstrap.NavGroup} the navgroup
4127 get: function(navId) {
4128 if (typeof(this.groups[navId]) == 'undefined') {
4130 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4132 return this.groups[navId] ;
4147 * @class Roo.bootstrap.NavItem
4148 * @extends Roo.bootstrap.Component
4149 * Bootstrap Navbar.NavItem class
4150 * @cfg {String} href link to
4151 * @cfg {String} html content of button
4152 * @cfg {String} badge text inside badge
4153 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4154 * @cfg {String} glyphicon name of glyphicon
4155 * @cfg {String} icon name of font awesome icon
4156 * @cfg {Boolean} active Is item active
4157 * @cfg {Boolean} disabled Is item disabled
4159 * @cfg {Boolean} preventDefault (true | false) default false
4160 * @cfg {String} tabId the tab that this item activates.
4161 * @cfg {String} tagtype (a|span) render as a href or span?
4162 * @cfg {Boolean} animateRef (true|false) link to element default false
4165 * Create a new Navbar Item
4166 * @param {Object} config The config object
4168 Roo.bootstrap.NavItem = function(config){
4169 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4174 * The raw click event for the entire grid.
4175 * @param {Roo.EventObject} e
4180 * Fires when the active item active state changes
4181 * @param {Roo.bootstrap.NavItem} this
4182 * @param {boolean} state the new state
4188 * Fires when scroll to element
4189 * @param {Roo.bootstrap.NavItem} this
4190 * @param {Object} options
4191 * @param {Roo.EventObject} e
4199 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4207 preventDefault : false,
4214 getAutoCreate : function(){
4223 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4225 if (this.disabled) {
4226 cfg.cls += ' disabled';
4229 if (this.href || this.html || this.glyphicon || this.icon) {
4233 href : this.href || "#",
4234 html: this.html || ''
4239 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4242 if(this.glyphicon) {
4243 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4248 cfg.cn[0].html += " <span class='caret'></span>";
4252 if (this.badge !== '') {
4254 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4262 initEvents: function()
4264 if (typeof (this.menu) != 'undefined') {
4265 this.menu.parentType = this.xtype;
4266 this.menu.triggerEl = this.el;
4267 this.menu = this.addxtype(Roo.apply({}, this.menu));
4270 this.el.select('a',true).on('click', this.onClick, this);
4272 if(this.tagtype == 'span'){
4273 this.el.select('span',true).on('click', this.onClick, this);
4276 // at this point parent should be available..
4277 this.parent().register(this);
4280 onClick : function(e)
4282 if (e.getTarget('.dropdown-menu-item')) {
4283 // did you click on a menu itemm.... - then don't trigger onclick..
4288 this.preventDefault ||
4291 Roo.log("NavItem - prevent Default?");
4295 if (this.disabled) {
4299 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4300 if (tg && tg.transition) {
4301 Roo.log("waiting for the transitionend");
4307 //Roo.log("fire event clicked");
4308 if(this.fireEvent('click', this, e) === false){
4312 if(this.tagtype == 'span'){
4316 //Roo.log(this.href);
4317 var ael = this.el.select('a',true).first();
4320 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4321 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4322 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4323 return; // ignore... - it's a 'hash' to another page.
4325 Roo.log("NavItem - prevent Default?");
4327 this.scrollToElement(e);
4331 var p = this.parent();
4333 if (['tabs','pills'].indexOf(p.type)!==-1) {
4334 if (typeof(p.setActiveItem) !== 'undefined') {
4335 p.setActiveItem(this);
4339 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4340 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4341 // remove the collapsed menu expand...
4342 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4346 isActive: function () {
4349 setActive : function(state, fire, is_was_active)
4351 if (this.active && !state && this.navId) {
4352 this.was_active = true;
4353 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4355 nv.clearWasActive(this);
4359 this.active = state;
4362 this.el.removeClass('active');
4363 } else if (!this.el.hasClass('active')) {
4364 this.el.addClass('active');
4367 this.fireEvent('changed', this, state);
4370 // show a panel if it's registered and related..
4372 if (!this.navId || !this.tabId || !state || is_was_active) {
4376 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4380 var pan = tg.getPanelByName(this.tabId);
4384 // if we can not flip to new panel - go back to old nav highlight..
4385 if (false == tg.showPanel(pan)) {
4386 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4388 var onav = nv.getWasActive();
4390 onav.setActive(true, false, true);
4399 // this should not be here...
4400 setDisabled : function(state)
4402 this.disabled = state;
4404 this.el.removeClass('disabled');
4405 } else if (!this.el.hasClass('disabled')) {
4406 this.el.addClass('disabled');
4412 * Fetch the element to display the tooltip on.
4413 * @return {Roo.Element} defaults to this.el
4415 tooltipEl : function()
4417 return this.el.select('' + this.tagtype + '', true).first();
4420 scrollToElement : function(e)
4422 var c = document.body;
4425 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4427 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4428 c = document.documentElement;
4431 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4437 var o = target.calcOffsetsTo(c);
4444 this.fireEvent('scrollto', this, options, e);
4446 Roo.get(c).scrollTo('top', options.value, true);
4459 * <span> icon </span>
4460 * <span> text </span>
4461 * <span>badge </span>
4465 * @class Roo.bootstrap.NavSidebarItem
4466 * @extends Roo.bootstrap.NavItem
4467 * Bootstrap Navbar.NavSidebarItem class
4468 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4469 * {bool} open is the menu open
4471 * Create a new Navbar Button
4472 * @param {Object} config The config object
4474 Roo.bootstrap.NavSidebarItem = function(config){
4475 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4480 * The raw click event for the entire grid.
4481 * @param {Roo.EventObject} e
4486 * Fires when the active item active state changes
4487 * @param {Roo.bootstrap.NavSidebarItem} this
4488 * @param {boolean} state the new state
4496 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4498 badgeWeight : 'default',
4502 getAutoCreate : function(){
4507 href : this.href || '#',
4519 html : this.html || ''
4524 cfg.cls += ' active';
4527 if (this.disabled) {
4528 cfg.cls += ' disabled';
4531 cfg.cls += ' open x-open';
4534 if (this.glyphicon || this.icon) {
4535 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4536 a.cn.push({ tag : 'i', cls : c }) ;
4541 if (this.badge !== '') {
4543 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4547 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4548 a.cls += 'dropdown-toggle treeview' ;
4556 initEvents : function()
4558 if (typeof (this.menu) != 'undefined') {
4559 this.menu.parentType = this.xtype;
4560 this.menu.triggerEl = this.el;
4561 this.menu = this.addxtype(Roo.apply({}, this.menu));
4564 this.el.on('click', this.onClick, this);
4567 if(this.badge !== ''){
4569 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4574 onClick : function(e)
4581 if(this.preventDefault){
4585 this.fireEvent('click', this);
4588 disable : function()
4590 this.setDisabled(true);
4595 this.setDisabled(false);
4598 setDisabled : function(state)
4600 if(this.disabled == state){
4604 this.disabled = state;
4607 this.el.addClass('disabled');
4611 this.el.removeClass('disabled');
4616 setActive : function(state)
4618 if(this.active == state){
4622 this.active = state;
4625 this.el.addClass('active');
4629 this.el.removeClass('active');
4634 isActive: function ()
4639 setBadge : function(str)
4645 this.badgeEl.dom.innerHTML = str;
4662 * @class Roo.bootstrap.Row
4663 * @extends Roo.bootstrap.Component
4664 * Bootstrap Row class (contains columns...)
4668 * @param {Object} config The config object
4671 Roo.bootstrap.Row = function(config){
4672 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4675 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4677 getAutoCreate : function(){
4696 * @class Roo.bootstrap.Element
4697 * @extends Roo.bootstrap.Component
4698 * Bootstrap Element class
4699 * @cfg {String} html contents of the element
4700 * @cfg {String} tag tag of the element
4701 * @cfg {String} cls class of the element
4702 * @cfg {Boolean} preventDefault (true|false) default false
4703 * @cfg {Boolean} clickable (true|false) default false
4706 * Create a new Element
4707 * @param {Object} config The config object
4710 Roo.bootstrap.Element = function(config){
4711 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4717 * When a element is chick
4718 * @param {Roo.bootstrap.Element} this
4719 * @param {Roo.EventObject} e
4725 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4730 preventDefault: false,
4733 getAutoCreate : function(){
4744 initEvents: function()
4746 Roo.bootstrap.Element.superclass.initEvents.call(this);
4749 this.el.on('click', this.onClick, this);
4754 onClick : function(e)
4756 if(this.preventDefault){
4760 this.fireEvent('click', this, e);
4763 getValue : function()
4765 return this.el.dom.innerHTML;
4768 setValue : function(value)
4770 this.el.dom.innerHTML = value;
4785 * @class Roo.bootstrap.Pagination
4786 * @extends Roo.bootstrap.Component
4787 * Bootstrap Pagination class
4788 * @cfg {String} size xs | sm | md | lg
4789 * @cfg {Boolean} inverse false | true
4792 * Create a new Pagination
4793 * @param {Object} config The config object
4796 Roo.bootstrap.Pagination = function(config){
4797 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4800 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4806 getAutoCreate : function(){
4812 cfg.cls += ' inverse';
4818 cfg.cls += " " + this.cls;
4836 * @class Roo.bootstrap.PaginationItem
4837 * @extends Roo.bootstrap.Component
4838 * Bootstrap PaginationItem class
4839 * @cfg {String} html text
4840 * @cfg {String} href the link
4841 * @cfg {Boolean} preventDefault (true | false) default true
4842 * @cfg {Boolean} active (true | false) default false
4843 * @cfg {Boolean} disabled default false
4847 * Create a new PaginationItem
4848 * @param {Object} config The config object
4852 Roo.bootstrap.PaginationItem = function(config){
4853 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4858 * The raw click event for the entire grid.
4859 * @param {Roo.EventObject} e
4865 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4869 preventDefault: true,
4874 getAutoCreate : function(){
4880 href : this.href ? this.href : '#',
4881 html : this.html ? this.html : ''
4891 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4895 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4901 initEvents: function() {
4903 this.el.on('click', this.onClick, this);
4906 onClick : function(e)
4908 Roo.log('PaginationItem on click ');
4909 if(this.preventDefault){
4917 this.fireEvent('click', this, e);
4933 * @class Roo.bootstrap.Slider
4934 * @extends Roo.bootstrap.Component
4935 * Bootstrap Slider class
4938 * Create a new Slider
4939 * @param {Object} config The config object
4942 Roo.bootstrap.Slider = function(config){
4943 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4946 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4948 getAutoCreate : function(){
4952 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4956 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4968 * Ext JS Library 1.1.1
4969 * Copyright(c) 2006-2007, Ext JS, LLC.
4971 * Originally Released Under LGPL - original licence link has changed is not relivant.
4974 * <script type="text/javascript">
4979 * @class Roo.grid.ColumnModel
4980 * @extends Roo.util.Observable
4981 * This is the default implementation of a ColumnModel used by the Grid. It defines
4982 * the columns in the grid.
4985 var colModel = new Roo.grid.ColumnModel([
4986 {header: "Ticker", width: 60, sortable: true, locked: true},
4987 {header: "Company Name", width: 150, sortable: true},
4988 {header: "Market Cap.", width: 100, sortable: true},
4989 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4990 {header: "Employees", width: 100, sortable: true, resizable: false}
4995 * The config options listed for this class are options which may appear in each
4996 * individual column definition.
4997 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4999 * @param {Object} config An Array of column config objects. See this class's
5000 * config objects for details.
5002 Roo.grid.ColumnModel = function(config){
5004 * The config passed into the constructor
5006 this.config = config;
5009 // if no id, create one
5010 // if the column does not have a dataIndex mapping,
5011 // map it to the order it is in the config
5012 for(var i = 0, len = config.length; i < len; i++){
5014 if(typeof c.dataIndex == "undefined"){
5017 if(typeof c.renderer == "string"){
5018 c.renderer = Roo.util.Format[c.renderer];
5020 if(typeof c.id == "undefined"){
5023 if(c.editor && c.editor.xtype){
5024 c.editor = Roo.factory(c.editor, Roo.grid);
5026 if(c.editor && c.editor.isFormField){
5027 c.editor = new Roo.grid.GridEditor(c.editor);
5029 this.lookup[c.id] = c;
5033 * The width of columns which have no width specified (defaults to 100)
5036 this.defaultWidth = 100;
5039 * Default sortable of columns which have no sortable specified (defaults to false)
5042 this.defaultSortable = false;
5046 * @event widthchange
5047 * Fires when the width of a column changes.
5048 * @param {ColumnModel} this
5049 * @param {Number} columnIndex The column index
5050 * @param {Number} newWidth The new width
5052 "widthchange": true,
5054 * @event headerchange
5055 * Fires when the text of a header changes.
5056 * @param {ColumnModel} this
5057 * @param {Number} columnIndex The column index
5058 * @param {Number} newText The new header text
5060 "headerchange": true,
5062 * @event hiddenchange
5063 * Fires when a column is hidden or "unhidden".
5064 * @param {ColumnModel} this
5065 * @param {Number} columnIndex The column index
5066 * @param {Boolean} hidden true if hidden, false otherwise
5068 "hiddenchange": true,
5070 * @event columnmoved
5071 * Fires when a column is moved.
5072 * @param {ColumnModel} this
5073 * @param {Number} oldIndex
5074 * @param {Number} newIndex
5076 "columnmoved" : true,
5078 * @event columlockchange
5079 * Fires when a column's locked state is changed
5080 * @param {ColumnModel} this
5081 * @param {Number} colIndex
5082 * @param {Boolean} locked true if locked
5084 "columnlockchange" : true
5086 Roo.grid.ColumnModel.superclass.constructor.call(this);
5088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5090 * @cfg {String} header The header text to display in the Grid view.
5093 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5094 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5095 * specified, the column's index is used as an index into the Record's data Array.
5098 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5099 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5102 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5103 * Defaults to the value of the {@link #defaultSortable} property.
5104 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5107 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5110 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5113 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5116 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5119 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5120 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5121 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5122 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5125 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5128 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5131 * @cfg {String} cursor (Optional)
5134 * @cfg {String} tooltip (Optional)
5137 * @cfg {Number} xs (Optional)
5140 * @cfg {Number} sm (Optional)
5143 * @cfg {Number} md (Optional)
5146 * @cfg {Number} lg (Optional)
5149 * Returns the id of the column at the specified index.
5150 * @param {Number} index The column index
5151 * @return {String} the id
5153 getColumnId : function(index){
5154 return this.config[index].id;
5158 * Returns the column for a specified id.
5159 * @param {String} id The column id
5160 * @return {Object} the column
5162 getColumnById : function(id){
5163 return this.lookup[id];
5168 * Returns the column for a specified dataIndex.
5169 * @param {String} dataIndex The column dataIndex
5170 * @return {Object|Boolean} the column or false if not found
5172 getColumnByDataIndex: function(dataIndex){
5173 var index = this.findColumnIndex(dataIndex);
5174 return index > -1 ? this.config[index] : false;
5178 * Returns the index for a specified column id.
5179 * @param {String} id The column id
5180 * @return {Number} the index, or -1 if not found
5182 getIndexById : function(id){
5183 for(var i = 0, len = this.config.length; i < len; i++){
5184 if(this.config[i].id == id){
5192 * Returns the index for a specified column dataIndex.
5193 * @param {String} dataIndex The column dataIndex
5194 * @return {Number} the index, or -1 if not found
5197 findColumnIndex : function(dataIndex){
5198 for(var i = 0, len = this.config.length; i < len; i++){
5199 if(this.config[i].dataIndex == dataIndex){
5207 moveColumn : function(oldIndex, newIndex){
5208 var c = this.config[oldIndex];
5209 this.config.splice(oldIndex, 1);
5210 this.config.splice(newIndex, 0, c);
5211 this.dataMap = null;
5212 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5215 isLocked : function(colIndex){
5216 return this.config[colIndex].locked === true;
5219 setLocked : function(colIndex, value, suppressEvent){
5220 if(this.isLocked(colIndex) == value){
5223 this.config[colIndex].locked = value;
5225 this.fireEvent("columnlockchange", this, colIndex, value);
5229 getTotalLockedWidth : function(){
5231 for(var i = 0; i < this.config.length; i++){
5232 if(this.isLocked(i) && !this.isHidden(i)){
5233 this.totalWidth += this.getColumnWidth(i);
5239 getLockedCount : function(){
5240 for(var i = 0, len = this.config.length; i < len; i++){
5241 if(!this.isLocked(i)){
5246 return this.config.length;
5250 * Returns the number of columns.
5253 getColumnCount : function(visibleOnly){
5254 if(visibleOnly === true){
5256 for(var i = 0, len = this.config.length; i < len; i++){
5257 if(!this.isHidden(i)){
5263 return this.config.length;
5267 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5268 * @param {Function} fn
5269 * @param {Object} scope (optional)
5270 * @return {Array} result
5272 getColumnsBy : function(fn, scope){
5274 for(var i = 0, len = this.config.length; i < len; i++){
5275 var c = this.config[i];
5276 if(fn.call(scope||this, c, i) === true){
5284 * Returns true if the specified column is sortable.
5285 * @param {Number} col The column index
5288 isSortable : function(col){
5289 if(typeof this.config[col].sortable == "undefined"){
5290 return this.defaultSortable;
5292 return this.config[col].sortable;
5296 * Returns the rendering (formatting) function defined for the column.
5297 * @param {Number} col The column index.
5298 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5300 getRenderer : function(col){
5301 if(!this.config[col].renderer){
5302 return Roo.grid.ColumnModel.defaultRenderer;
5304 return this.config[col].renderer;
5308 * Sets the rendering (formatting) function for a column.
5309 * @param {Number} col The column index
5310 * @param {Function} fn The function to use to process the cell's raw data
5311 * to return HTML markup for the grid view. The render function is called with
5312 * the following parameters:<ul>
5313 * <li>Data value.</li>
5314 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5315 * <li>css A CSS style string to apply to the table cell.</li>
5316 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5317 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5318 * <li>Row index</li>
5319 * <li>Column index</li>
5320 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5322 setRenderer : function(col, fn){
5323 this.config[col].renderer = fn;
5327 * Returns the width for the specified column.
5328 * @param {Number} col The column index
5331 getColumnWidth : function(col){
5332 return this.config[col].width * 1 || this.defaultWidth;
5336 * Sets the width for a column.
5337 * @param {Number} col The column index
5338 * @param {Number} width The new width
5340 setColumnWidth : function(col, width, suppressEvent){
5341 this.config[col].width = width;
5342 this.totalWidth = null;
5344 this.fireEvent("widthchange", this, col, width);
5349 * Returns the total width of all columns.
5350 * @param {Boolean} includeHidden True to include hidden column widths
5353 getTotalWidth : function(includeHidden){
5354 if(!this.totalWidth){
5355 this.totalWidth = 0;
5356 for(var i = 0, len = this.config.length; i < len; i++){
5357 if(includeHidden || !this.isHidden(i)){
5358 this.totalWidth += this.getColumnWidth(i);
5362 return this.totalWidth;
5366 * Returns the header for the specified column.
5367 * @param {Number} col The column index
5370 getColumnHeader : function(col){
5371 return this.config[col].header;
5375 * Sets the header for a column.
5376 * @param {Number} col The column index
5377 * @param {String} header The new header
5379 setColumnHeader : function(col, header){
5380 this.config[col].header = header;
5381 this.fireEvent("headerchange", this, col, header);
5385 * Returns the tooltip for the specified column.
5386 * @param {Number} col The column index
5389 getColumnTooltip : function(col){
5390 return this.config[col].tooltip;
5393 * Sets the tooltip for a column.
5394 * @param {Number} col The column index
5395 * @param {String} tooltip The new tooltip
5397 setColumnTooltip : function(col, tooltip){
5398 this.config[col].tooltip = tooltip;
5402 * Returns the dataIndex for the specified column.
5403 * @param {Number} col The column index
5406 getDataIndex : function(col){
5407 return this.config[col].dataIndex;
5411 * Sets the dataIndex for a column.
5412 * @param {Number} col The column index
5413 * @param {Number} dataIndex The new dataIndex
5415 setDataIndex : function(col, dataIndex){
5416 this.config[col].dataIndex = dataIndex;
5422 * Returns true if the cell is editable.
5423 * @param {Number} colIndex The column index
5424 * @param {Number} rowIndex The row index - this is nto actually used..?
5427 isCellEditable : function(colIndex, rowIndex){
5428 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5432 * Returns the editor defined for the cell/column.
5433 * return false or null to disable editing.
5434 * @param {Number} colIndex The column index
5435 * @param {Number} rowIndex The row index
5438 getCellEditor : function(colIndex, rowIndex){
5439 return this.config[colIndex].editor;
5443 * Sets if a column is editable.
5444 * @param {Number} col The column index
5445 * @param {Boolean} editable True if the column is editable
5447 setEditable : function(col, editable){
5448 this.config[col].editable = editable;
5453 * Returns true if the column is hidden.
5454 * @param {Number} colIndex The column index
5457 isHidden : function(colIndex){
5458 return this.config[colIndex].hidden;
5463 * Returns true if the column width cannot be changed
5465 isFixed : function(colIndex){
5466 return this.config[colIndex].fixed;
5470 * Returns true if the column can be resized
5473 isResizable : function(colIndex){
5474 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5477 * Sets if a column is hidden.
5478 * @param {Number} colIndex The column index
5479 * @param {Boolean} hidden True if the column is hidden
5481 setHidden : function(colIndex, hidden){
5482 this.config[colIndex].hidden = hidden;
5483 this.totalWidth = null;
5484 this.fireEvent("hiddenchange", this, colIndex, hidden);
5488 * Sets the editor for a column.
5489 * @param {Number} col The column index
5490 * @param {Object} editor The editor object
5492 setEditor : function(col, editor){
5493 this.config[col].editor = editor;
5497 Roo.grid.ColumnModel.defaultRenderer = function(value){
5498 if(typeof value == "string" && value.length < 1){
5504 // Alias for backwards compatibility
5505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5508 * Ext JS Library 1.1.1
5509 * Copyright(c) 2006-2007, Ext JS, LLC.
5511 * Originally Released Under LGPL - original licence link has changed is not relivant.
5514 * <script type="text/javascript">
5518 * @class Roo.LoadMask
5519 * A simple utility class for generically masking elements while loading data. If the element being masked has
5520 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5521 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5522 * element's UpdateManager load indicator and will be destroyed after the initial load.
5524 * Create a new LoadMask
5525 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5526 * @param {Object} config The config object
5528 Roo.LoadMask = function(el, config){
5529 this.el = Roo.get(el);
5530 Roo.apply(this, config);
5532 this.store.on('beforeload', this.onBeforeLoad, this);
5533 this.store.on('load', this.onLoad, this);
5534 this.store.on('loadexception', this.onLoadException, this);
5535 this.removeMask = false;
5537 var um = this.el.getUpdateManager();
5538 um.showLoadIndicator = false; // disable the default indicator
5539 um.on('beforeupdate', this.onBeforeLoad, this);
5540 um.on('update', this.onLoad, this);
5541 um.on('failure', this.onLoad, this);
5542 this.removeMask = true;
5546 Roo.LoadMask.prototype = {
5548 * @cfg {Boolean} removeMask
5549 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5550 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5554 * The text to display in a centered loading message box (defaults to 'Loading...')
5558 * @cfg {String} msgCls
5559 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5561 msgCls : 'x-mask-loading',
5564 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5570 * Disables the mask to prevent it from being displayed
5572 disable : function(){
5573 this.disabled = true;
5577 * Enables the mask so that it can be displayed
5579 enable : function(){
5580 this.disabled = false;
5583 onLoadException : function()
5587 if (typeof(arguments[3]) != 'undefined') {
5588 Roo.MessageBox.alert("Error loading",arguments[3]);
5592 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5593 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5602 this.el.unmask(this.removeMask);
5607 this.el.unmask(this.removeMask);
5611 onBeforeLoad : function(){
5613 this.el.mask(this.msg, this.msgCls);
5618 destroy : function(){
5620 this.store.un('beforeload', this.onBeforeLoad, this);
5621 this.store.un('load', this.onLoad, this);
5622 this.store.un('loadexception', this.onLoadException, this);
5624 var um = this.el.getUpdateManager();
5625 um.un('beforeupdate', this.onBeforeLoad, this);
5626 um.un('update', this.onLoad, this);
5627 um.un('failure', this.onLoad, this);
5638 * @class Roo.bootstrap.Table
5639 * @extends Roo.bootstrap.Component
5640 * Bootstrap Table class
5641 * @cfg {String} cls table class
5642 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5643 * @cfg {String} bgcolor Specifies the background color for a table
5644 * @cfg {Number} border Specifies whether the table cells should have borders or not
5645 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5646 * @cfg {Number} cellspacing Specifies the space between cells
5647 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5648 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5649 * @cfg {String} sortable Specifies that the table should be sortable
5650 * @cfg {String} summary Specifies a summary of the content of a table
5651 * @cfg {Number} width Specifies the width of a table
5652 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5654 * @cfg {boolean} striped Should the rows be alternative striped
5655 * @cfg {boolean} bordered Add borders to the table
5656 * @cfg {boolean} hover Add hover highlighting
5657 * @cfg {boolean} condensed Format condensed
5658 * @cfg {boolean} responsive Format condensed
5659 * @cfg {Boolean} loadMask (true|false) default false
5660 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5661 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5662 * @cfg {Boolean} rowSelection (true|false) default false
5663 * @cfg {Boolean} cellSelection (true|false) default false
5664 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5668 * Create a new Table
5669 * @param {Object} config The config object
5672 Roo.bootstrap.Table = function(config){
5673 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5676 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5677 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5678 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5679 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5683 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5684 this.sm = this.selModel;
5685 this.sm.xmodule = this.xmodule || false;
5687 if (this.cm && typeof(this.cm.config) == 'undefined') {
5688 this.colModel = new Roo.grid.ColumnModel(this.cm);
5689 this.cm = this.colModel;
5690 this.cm.xmodule = this.xmodule || false;
5693 this.store= Roo.factory(this.store, Roo.data);
5694 this.ds = this.store;
5695 this.ds.xmodule = this.xmodule || false;
5698 if (this.footer && this.store) {
5699 this.footer.dataSource = this.ds;
5700 this.footer = Roo.factory(this.footer);
5707 * Fires when a cell is clicked
5708 * @param {Roo.bootstrap.Table} this
5709 * @param {Roo.Element} el
5710 * @param {Number} rowIndex
5711 * @param {Number} columnIndex
5712 * @param {Roo.EventObject} e
5716 * @event celldblclick
5717 * Fires when a cell is double clicked
5718 * @param {Roo.bootstrap.Table} this
5719 * @param {Roo.Element} el
5720 * @param {Number} rowIndex
5721 * @param {Number} columnIndex
5722 * @param {Roo.EventObject} e
5724 "celldblclick" : true,
5727 * Fires when a row is clicked
5728 * @param {Roo.bootstrap.Table} this
5729 * @param {Roo.Element} el
5730 * @param {Number} rowIndex
5731 * @param {Roo.EventObject} e
5735 * @event rowdblclick
5736 * Fires when a row is double clicked
5737 * @param {Roo.bootstrap.Table} this
5738 * @param {Roo.Element} el
5739 * @param {Number} rowIndex
5740 * @param {Roo.EventObject} e
5742 "rowdblclick" : true,
5745 * Fires when a mouseover occur
5746 * @param {Roo.bootstrap.Table} this
5747 * @param {Roo.Element} el
5748 * @param {Number} rowIndex
5749 * @param {Number} columnIndex
5750 * @param {Roo.EventObject} e
5755 * Fires when a mouseout occur
5756 * @param {Roo.bootstrap.Table} this
5757 * @param {Roo.Element} el
5758 * @param {Number} rowIndex
5759 * @param {Number} columnIndex
5760 * @param {Roo.EventObject} e
5765 * Fires when a row is rendered, so you can change add a style to it.
5766 * @param {Roo.bootstrap.Table} this
5767 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5771 * @event rowsrendered
5772 * Fires when all the rows have been rendered
5773 * @param {Roo.bootstrap.Table} this
5775 'rowsrendered' : true,
5777 * @event contextmenu
5778 * The raw contextmenu event for the entire grid.
5779 * @param {Roo.EventObject} e
5781 "contextmenu" : true,
5783 * @event rowcontextmenu
5784 * Fires when a row is right clicked
5785 * @param {Roo.bootstrap.Table} this
5786 * @param {Number} rowIndex
5787 * @param {Roo.EventObject} e
5789 "rowcontextmenu" : true,
5791 * @event cellcontextmenu
5792 * Fires when a cell is right clicked
5793 * @param {Roo.bootstrap.Table} this
5794 * @param {Number} rowIndex
5795 * @param {Number} cellIndex
5796 * @param {Roo.EventObject} e
5798 "cellcontextmenu" : true,
5800 * @event headercontextmenu
5801 * Fires when a header is right clicked
5802 * @param {Roo.bootstrap.Table} this
5803 * @param {Number} columnIndex
5804 * @param {Roo.EventObject} e
5806 "headercontextmenu" : true
5810 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5835 rowSelection : false,
5836 cellSelection : false,
5839 // Roo.Element - the tbody
5842 getAutoCreate : function(){
5843 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5852 cfg.cls += ' table-striped';
5856 cfg.cls += ' table-hover';
5858 if (this.bordered) {
5859 cfg.cls += ' table-bordered';
5861 if (this.condensed) {
5862 cfg.cls += ' table-condensed';
5864 if (this.responsive) {
5865 cfg.cls += ' table-responsive';
5869 cfg.cls+= ' ' +this.cls;
5872 // this lot should be simplifed...
5875 cfg.align=this.align;
5878 cfg.bgcolor=this.bgcolor;
5881 cfg.border=this.border;
5883 if (this.cellpadding) {
5884 cfg.cellpadding=this.cellpadding;
5886 if (this.cellspacing) {
5887 cfg.cellspacing=this.cellspacing;
5890 cfg.frame=this.frame;
5893 cfg.rules=this.rules;
5895 if (this.sortable) {
5896 cfg.sortable=this.sortable;
5899 cfg.summary=this.summary;
5902 cfg.width=this.width;
5905 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5908 if(this.store || this.cm){
5909 if(this.headerShow){
5910 cfg.cn.push(this.renderHeader());
5913 cfg.cn.push(this.renderBody());
5915 if(this.footerShow){
5916 cfg.cn.push(this.renderFooter());
5919 cfg.cls+= ' TableGrid';
5922 return { cn : [ cfg ] };
5925 initEvents : function()
5927 if(!this.store || !this.cm){
5931 //Roo.log('initEvents with ds!!!!');
5933 this.mainBody = this.el.select('tbody', true).first();
5938 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5939 e.on('click', _this.sort, _this);
5942 this.el.on("click", this.onClick, this);
5943 this.el.on("dblclick", this.onDblClick, this);
5945 // why is this done????? = it breaks dialogs??
5946 //this.parent().el.setStyle('position', 'relative');
5950 this.footer.parentId = this.id;
5951 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5954 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5956 this.store.on('load', this.onLoad, this);
5957 this.store.on('beforeload', this.onBeforeLoad, this);
5958 this.store.on('update', this.onUpdate, this);
5959 this.store.on('add', this.onAdd, this);
5961 this.el.on("contextmenu", this.onContextMenu, this);
5965 onContextMenu : function(e, t)
5967 this.processEvent("contextmenu", e);
5970 processEvent : function(name, e)
5972 if (name != 'touchstart' ) {
5973 this.fireEvent(name, e);
5976 var t = e.getTarget();
5978 var cell = Roo.get(t);
5984 if(cell.findParent('tfoot', false, true)){
5988 if(cell.findParent('thead', false, true)){
5990 if(e.getTarget().nodeName.toLowerCase() != 'th'){
5991 cell = Roo.get(t).findParent('th', false, true);
5994 var cellIndex = cell.dom.cellIndex;
5996 var ename = name == 'touchstart' ? 'click' : name;
5997 this.fireEvent("header" + ename, this, cellIndex, e);
6002 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6003 cell = Roo.get(t).findParent('td', false, true);
6006 var row = cell.findParent('tr', false, true);
6007 var cellIndex = cell.dom.cellIndex;
6008 var rowIndex = row.dom.rowIndex - 1;
6012 this.fireEvent("row" + name, this, rowIndex, e);
6016 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6022 onMouseover : function(e, el)
6024 var cell = Roo.get(el);
6030 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6031 cell = cell.findParent('td', false, true);
6034 var row = cell.findParent('tr', false, true);
6035 var cellIndex = cell.dom.cellIndex;
6036 var rowIndex = row.dom.rowIndex - 1; // start from 0
6038 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6042 onMouseout : function(e, el)
6044 var cell = Roo.get(el);
6050 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6051 cell = cell.findParent('td', false, true);
6054 var row = cell.findParent('tr', false, true);
6055 var cellIndex = cell.dom.cellIndex;
6056 var rowIndex = row.dom.rowIndex - 1; // start from 0
6058 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6062 onClick : function(e, el)
6064 var cell = Roo.get(el);
6066 if(!cell || (!this.cellSelection && !this.rowSelection)){
6070 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6071 cell = cell.findParent('td', false, true);
6074 if(!cell || typeof(cell) == 'undefined'){
6078 var row = cell.findParent('tr', false, true);
6080 if(!row || typeof(row) == 'undefined'){
6084 var cellIndex = cell.dom.cellIndex;
6085 var rowIndex = this.getRowIndex(row);
6087 // why??? - should these not be based on SelectionModel?
6088 if(this.cellSelection){
6089 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6092 if(this.rowSelection){
6093 this.fireEvent('rowclick', this, row, rowIndex, e);
6099 onDblClick : function(e,el)
6101 var cell = Roo.get(el);
6103 if(!cell || (!this.CellSelection && !this.RowSelection)){
6107 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6108 cell = cell.findParent('td', false, true);
6111 if(!cell || typeof(cell) == 'undefined'){
6115 var row = cell.findParent('tr', false, true);
6117 if(!row || typeof(row) == 'undefined'){
6121 var cellIndex = cell.dom.cellIndex;
6122 var rowIndex = this.getRowIndex(row);
6124 if(this.CellSelection){
6125 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6128 if(this.RowSelection){
6129 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6133 sort : function(e,el)
6135 var col = Roo.get(el);
6137 if(!col.hasClass('sortable')){
6141 var sort = col.attr('sort');
6144 if(col.hasClass('glyphicon-arrow-up')){
6148 this.store.sortInfo = {field : sort, direction : dir};
6151 Roo.log("calling footer first");
6152 this.footer.onClick('first');
6155 this.store.load({ params : { start : 0 } });
6159 renderHeader : function()
6168 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6170 var config = cm.config[i];
6175 html: cm.getColumnHeader(i)
6180 if(typeof(config.lgHeader) != 'undefined'){
6181 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6184 if(typeof(config.mdHeader) != 'undefined'){
6185 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6188 if(typeof(config.smHeader) != 'undefined'){
6189 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6192 if(typeof(config.xsHeader) != 'undefined'){
6193 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6200 if(typeof(config.tooltip) != 'undefined'){
6201 c.tooltip = config.tooltip;
6204 if(typeof(config.colspan) != 'undefined'){
6205 c.colspan = config.colspan;
6208 if(typeof(config.hidden) != 'undefined' && config.hidden){
6209 c.style += ' display:none;';
6212 if(typeof(config.dataIndex) != 'undefined'){
6213 c.sort = config.dataIndex;
6216 if(typeof(config.sortable) != 'undefined' && config.sortable){
6220 if(typeof(config.align) != 'undefined' && config.align.length){
6221 c.style += ' text-align:' + config.align + ';';
6224 if(typeof(config.width) != 'undefined'){
6225 c.style += ' width:' + config.width + 'px;';
6228 if(typeof(config.cls) != 'undefined'){
6229 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6232 ['xs','sm','md','lg'].map(function(size){
6234 if(typeof(config[size]) == 'undefined'){
6238 if (!config[size]) { // 0 = hidden
6239 c.cls += ' hidden-' + size;
6243 c.cls += ' col-' + size + '-' + config[size];
6253 renderBody : function()
6263 colspan : this.cm.getColumnCount()
6273 renderFooter : function()
6283 colspan : this.cm.getColumnCount()
6297 // Roo.log('ds onload');
6302 var ds = this.store;
6304 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6305 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6307 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6308 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6311 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6312 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6316 var tbody = this.mainBody;
6318 if(ds.getCount() > 0){
6319 ds.data.each(function(d,rowIndex){
6320 var row = this.renderRow(cm, ds, rowIndex);
6322 tbody.createChild(row);
6326 if(row.cellObjects.length){
6327 Roo.each(row.cellObjects, function(r){
6328 _this.renderCellObject(r);
6335 Roo.each(this.el.select('tbody td', true).elements, function(e){
6336 e.on('mouseover', _this.onMouseover, _this);
6339 Roo.each(this.el.select('tbody td', true).elements, function(e){
6340 e.on('mouseout', _this.onMouseout, _this);
6342 this.fireEvent('rowsrendered', this);
6343 //if(this.loadMask){
6344 // this.maskEl.hide();
6349 onUpdate : function(ds,record)
6351 this.refreshRow(record);
6354 onRemove : function(ds, record, index, isUpdate){
6355 if(isUpdate !== true){
6356 this.fireEvent("beforerowremoved", this, index, record);
6358 var bt = this.mainBody.dom;
6360 var rows = this.el.select('tbody > tr', true).elements;
6362 if(typeof(rows[index]) != 'undefined'){
6363 bt.removeChild(rows[index].dom);
6366 // if(bt.rows[index]){
6367 // bt.removeChild(bt.rows[index]);
6370 if(isUpdate !== true){
6371 //this.stripeRows(index);
6372 //this.syncRowHeights(index, index);
6374 this.fireEvent("rowremoved", this, index, record);
6378 onAdd : function(ds, records, rowIndex)
6380 //Roo.log('on Add called');
6381 // - note this does not handle multiple adding very well..
6382 var bt = this.mainBody.dom;
6383 for (var i =0 ; i < records.length;i++) {
6384 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6385 //Roo.log(records[i]);
6386 //Roo.log(this.store.getAt(rowIndex+i));
6387 this.insertRow(this.store, rowIndex + i, false);
6394 refreshRow : function(record){
6395 var ds = this.store, index;
6396 if(typeof record == 'number'){
6398 record = ds.getAt(index);
6400 index = ds.indexOf(record);
6402 this.insertRow(ds, index, true);
6403 this.onRemove(ds, record, index+1, true);
6404 //this.syncRowHeights(index, index);
6406 this.fireEvent("rowupdated", this, index, record);
6409 insertRow : function(dm, rowIndex, isUpdate){
6412 this.fireEvent("beforerowsinserted", this, rowIndex);
6414 //var s = this.getScrollState();
6415 var row = this.renderRow(this.cm, this.store, rowIndex);
6416 // insert before rowIndex..
6417 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6421 if(row.cellObjects.length){
6422 Roo.each(row.cellObjects, function(r){
6423 _this.renderCellObject(r);
6428 this.fireEvent("rowsinserted", this, rowIndex);
6429 //this.syncRowHeights(firstRow, lastRow);
6430 //this.stripeRows(firstRow);
6437 getRowDom : function(rowIndex)
6439 var rows = this.el.select('tbody > tr', true).elements;
6441 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6444 // returns the object tree for a tr..
6447 renderRow : function(cm, ds, rowIndex)
6450 var d = ds.getAt(rowIndex);
6457 var cellObjects = [];
6459 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6460 var config = cm.config[i];
6462 var renderer = cm.getRenderer(i);
6466 if(typeof(renderer) !== 'undefined'){
6467 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6469 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6470 // and are rendered into the cells after the row is rendered - using the id for the element.
6472 if(typeof(value) === 'object'){
6482 rowIndex : rowIndex,
6487 this.fireEvent('rowclass', this, rowcfg);
6491 cls : rowcfg.rowClass,
6493 html: (typeof(value) === 'object') ? '' : value
6500 if(typeof(config.colspan) != 'undefined'){
6501 td.colspan = config.colspan;
6504 if(typeof(config.hidden) != 'undefined' && config.hidden){
6505 td.style += ' display:none;';
6508 if(typeof(config.align) != 'undefined' && config.align.length){
6509 td.style += ' text-align:' + config.align + ';';
6512 if(typeof(config.width) != 'undefined'){
6513 td.style += ' width:' + config.width + 'px;';
6516 if(typeof(config.cursor) != 'undefined'){
6517 td.style += ' cursor:' + config.cursor + ';';
6520 if(typeof(config.cls) != 'undefined'){
6521 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6524 ['xs','sm','md','lg'].map(function(size){
6526 if(typeof(config[size]) == 'undefined'){
6530 if (!config[size]) { // 0 = hidden
6531 td.cls += ' hidden-' + size;
6535 td.cls += ' col-' + size + '-' + config[size];
6543 row.cellObjects = cellObjects;
6551 onBeforeLoad : function()
6553 //Roo.log('ds onBeforeLoad');
6557 //if(this.loadMask){
6558 // this.maskEl.show();
6566 this.el.select('tbody', true).first().dom.innerHTML = '';
6569 * Show or hide a row.
6570 * @param {Number} rowIndex to show or hide
6571 * @param {Boolean} state hide
6573 setRowVisibility : function(rowIndex, state)
6575 var bt = this.mainBody.dom;
6577 var rows = this.el.select('tbody > tr', true).elements;
6579 if(typeof(rows[rowIndex]) == 'undefined'){
6582 rows[rowIndex].dom.style.display = state ? '' : 'none';
6586 getSelectionModel : function(){
6588 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6590 return this.selModel;
6593 * Render the Roo.bootstrap object from renderder
6595 renderCellObject : function(r)
6599 var t = r.cfg.render(r.container);
6602 Roo.each(r.cfg.cn, function(c){
6604 container: t.getChildContainer(),
6607 _this.renderCellObject(child);
6612 getRowIndex : function(row)
6616 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6639 * @class Roo.bootstrap.TableCell
6640 * @extends Roo.bootstrap.Component
6641 * Bootstrap TableCell class
6642 * @cfg {String} html cell contain text
6643 * @cfg {String} cls cell class
6644 * @cfg {String} tag cell tag (td|th) default td
6645 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6646 * @cfg {String} align Aligns the content in a cell
6647 * @cfg {String} axis Categorizes cells
6648 * @cfg {String} bgcolor Specifies the background color of a cell
6649 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6650 * @cfg {Number} colspan Specifies the number of columns a cell should span
6651 * @cfg {String} headers Specifies one or more header cells a cell is related to
6652 * @cfg {Number} height Sets the height of a cell
6653 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6654 * @cfg {Number} rowspan Sets the number of rows a cell should span
6655 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6656 * @cfg {String} valign Vertical aligns the content in a cell
6657 * @cfg {Number} width Specifies the width of a cell
6660 * Create a new TableCell
6661 * @param {Object} config The config object
6664 Roo.bootstrap.TableCell = function(config){
6665 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6668 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6688 getAutoCreate : function(){
6689 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6709 cfg.align=this.align
6715 cfg.bgcolor=this.bgcolor
6718 cfg.charoff=this.charoff
6721 cfg.colspan=this.colspan
6724 cfg.headers=this.headers
6727 cfg.height=this.height
6730 cfg.nowrap=this.nowrap
6733 cfg.rowspan=this.rowspan
6736 cfg.scope=this.scope
6739 cfg.valign=this.valign
6742 cfg.width=this.width
6761 * @class Roo.bootstrap.TableRow
6762 * @extends Roo.bootstrap.Component
6763 * Bootstrap TableRow class
6764 * @cfg {String} cls row class
6765 * @cfg {String} align Aligns the content in a table row
6766 * @cfg {String} bgcolor Specifies a background color for a table row
6767 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6768 * @cfg {String} valign Vertical aligns the content in a table row
6771 * Create a new TableRow
6772 * @param {Object} config The config object
6775 Roo.bootstrap.TableRow = function(config){
6776 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6779 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6787 getAutoCreate : function(){
6788 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6798 cfg.align = this.align;
6801 cfg.bgcolor = this.bgcolor;
6804 cfg.charoff = this.charoff;
6807 cfg.valign = this.valign;
6825 * @class Roo.bootstrap.TableBody
6826 * @extends Roo.bootstrap.Component
6827 * Bootstrap TableBody class
6828 * @cfg {String} cls element class
6829 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6830 * @cfg {String} align Aligns the content inside the element
6831 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6832 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6835 * Create a new TableBody
6836 * @param {Object} config The config object
6839 Roo.bootstrap.TableBody = function(config){
6840 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6843 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6851 getAutoCreate : function(){
6852 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6866 cfg.align = this.align;
6869 cfg.charoff = this.charoff;
6872 cfg.valign = this.valign;
6879 // initEvents : function()
6886 // this.store = Roo.factory(this.store, Roo.data);
6887 // this.store.on('load', this.onLoad, this);
6889 // this.store.load();
6893 // onLoad: function ()
6895 // this.fireEvent('load', this);
6905 * Ext JS Library 1.1.1
6906 * Copyright(c) 2006-2007, Ext JS, LLC.
6908 * Originally Released Under LGPL - original licence link has changed is not relivant.
6911 * <script type="text/javascript">
6914 // as we use this in bootstrap.
6915 Roo.namespace('Roo.form');
6917 * @class Roo.form.Action
6918 * Internal Class used to handle form actions
6920 * @param {Roo.form.BasicForm} el The form element or its id
6921 * @param {Object} config Configuration options
6926 // define the action interface
6927 Roo.form.Action = function(form, options){
6929 this.options = options || {};
6932 * Client Validation Failed
6935 Roo.form.Action.CLIENT_INVALID = 'client';
6937 * Server Validation Failed
6940 Roo.form.Action.SERVER_INVALID = 'server';
6942 * Connect to Server Failed
6945 Roo.form.Action.CONNECT_FAILURE = 'connect';
6947 * Reading Data from Server Failed
6950 Roo.form.Action.LOAD_FAILURE = 'load';
6952 Roo.form.Action.prototype = {
6954 failureType : undefined,
6955 response : undefined,
6959 run : function(options){
6964 success : function(response){
6969 handleResponse : function(response){
6973 // default connection failure
6974 failure : function(response){
6976 this.response = response;
6977 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6978 this.form.afterAction(this, false);
6981 processResponse : function(response){
6982 this.response = response;
6983 if(!response.responseText){
6986 this.result = this.handleResponse(response);
6990 // utility functions used internally
6991 getUrl : function(appendParams){
6992 var url = this.options.url || this.form.url || this.form.el.dom.action;
6994 var p = this.getParams();
6996 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7002 getMethod : function(){
7003 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7006 getParams : function(){
7007 var bp = this.form.baseParams;
7008 var p = this.options.params;
7010 if(typeof p == "object"){
7011 p = Roo.urlEncode(Roo.applyIf(p, bp));
7012 }else if(typeof p == 'string' && bp){
7013 p += '&' + Roo.urlEncode(bp);
7016 p = Roo.urlEncode(bp);
7021 createCallback : function(){
7023 success: this.success,
7024 failure: this.failure,
7026 timeout: (this.form.timeout*1000),
7027 upload: this.form.fileUpload ? this.success : undefined
7032 Roo.form.Action.Submit = function(form, options){
7033 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7036 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7039 haveProgress : false,
7040 uploadComplete : false,
7042 // uploadProgress indicator.
7043 uploadProgress : function()
7045 if (!this.form.progressUrl) {
7049 if (!this.haveProgress) {
7050 Roo.MessageBox.progress("Uploading", "Uploading");
7052 if (this.uploadComplete) {
7053 Roo.MessageBox.hide();
7057 this.haveProgress = true;
7059 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7061 var c = new Roo.data.Connection();
7063 url : this.form.progressUrl,
7068 success : function(req){
7069 //console.log(data);
7073 rdata = Roo.decode(req.responseText)
7075 Roo.log("Invalid data from server..");
7079 if (!rdata || !rdata.success) {
7081 Roo.MessageBox.alert(Roo.encode(rdata));
7084 var data = rdata.data;
7086 if (this.uploadComplete) {
7087 Roo.MessageBox.hide();
7092 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7093 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7096 this.uploadProgress.defer(2000,this);
7099 failure: function(data) {
7100 Roo.log('progress url failed ');
7111 // run get Values on the form, so it syncs any secondary forms.
7112 this.form.getValues();
7114 var o = this.options;
7115 var method = this.getMethod();
7116 var isPost = method == 'POST';
7117 if(o.clientValidation === false || this.form.isValid()){
7119 if (this.form.progressUrl) {
7120 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7121 (new Date() * 1) + '' + Math.random());
7126 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7127 form:this.form.el.dom,
7128 url:this.getUrl(!isPost),
7130 params:isPost ? this.getParams() : null,
7131 isUpload: this.form.fileUpload
7134 this.uploadProgress();
7136 }else if (o.clientValidation !== false){ // client validation failed
7137 this.failureType = Roo.form.Action.CLIENT_INVALID;
7138 this.form.afterAction(this, false);
7142 success : function(response)
7144 this.uploadComplete= true;
7145 if (this.haveProgress) {
7146 Roo.MessageBox.hide();
7150 var result = this.processResponse(response);
7151 if(result === true || result.success){
7152 this.form.afterAction(this, true);
7156 this.form.markInvalid(result.errors);
7157 this.failureType = Roo.form.Action.SERVER_INVALID;
7159 this.form.afterAction(this, false);
7161 failure : function(response)
7163 this.uploadComplete= true;
7164 if (this.haveProgress) {
7165 Roo.MessageBox.hide();
7168 this.response = response;
7169 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7170 this.form.afterAction(this, false);
7173 handleResponse : function(response){
7174 if(this.form.errorReader){
7175 var rs = this.form.errorReader.read(response);
7178 for(var i = 0, len = rs.records.length; i < len; i++) {
7179 var r = rs.records[i];
7183 if(errors.length < 1){
7187 success : rs.success,
7193 ret = Roo.decode(response.responseText);
7197 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7207 Roo.form.Action.Load = function(form, options){
7208 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7209 this.reader = this.form.reader;
7212 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7217 Roo.Ajax.request(Roo.apply(
7218 this.createCallback(), {
7219 method:this.getMethod(),
7220 url:this.getUrl(false),
7221 params:this.getParams()
7225 success : function(response){
7227 var result = this.processResponse(response);
7228 if(result === true || !result.success || !result.data){
7229 this.failureType = Roo.form.Action.LOAD_FAILURE;
7230 this.form.afterAction(this, false);
7233 this.form.clearInvalid();
7234 this.form.setValues(result.data);
7235 this.form.afterAction(this, true);
7238 handleResponse : function(response){
7239 if(this.form.reader){
7240 var rs = this.form.reader.read(response);
7241 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7243 success : rs.success,
7247 return Roo.decode(response.responseText);
7251 Roo.form.Action.ACTION_TYPES = {
7252 'load' : Roo.form.Action.Load,
7253 'submit' : Roo.form.Action.Submit
7262 * @class Roo.bootstrap.Form
7263 * @extends Roo.bootstrap.Component
7264 * Bootstrap Form class
7265 * @cfg {String} method GET | POST (default POST)
7266 * @cfg {String} labelAlign top | left (default top)
7267 * @cfg {String} align left | right - for navbars
7268 * @cfg {Boolean} loadMask load mask when submit (default true)
7273 * @param {Object} config The config object
7277 Roo.bootstrap.Form = function(config){
7278 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7281 * @event clientvalidation
7282 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7283 * @param {Form} this
7284 * @param {Boolean} valid true if the form has passed client-side validation
7286 clientvalidation: true,
7288 * @event beforeaction
7289 * Fires before any action is performed. Return false to cancel the action.
7290 * @param {Form} this
7291 * @param {Action} action The action to be performed
7295 * @event actionfailed
7296 * Fires when an action fails.
7297 * @param {Form} this
7298 * @param {Action} action The action that failed
7300 actionfailed : true,
7302 * @event actioncomplete
7303 * Fires when an action is completed.
7304 * @param {Form} this
7305 * @param {Action} action The action that completed
7307 actioncomplete : true
7312 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7315 * @cfg {String} method
7316 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7321 * The URL to use for form actions if one isn't supplied in the action options.
7324 * @cfg {Boolean} fileUpload
7325 * Set to true if this form is a file upload.
7329 * @cfg {Object} baseParams
7330 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7334 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7338 * @cfg {Sting} align (left|right) for navbar forms
7343 activeAction : null,
7346 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7347 * element by passing it or its id or mask the form itself by passing in true.
7350 waitMsgTarget : false,
7354 getAutoCreate : function(){
7358 method : this.method || 'POST',
7359 id : this.id || Roo.id(),
7362 if (this.parent().xtype.match(/^Nav/)) {
7363 cfg.cls = 'navbar-form navbar-' + this.align;
7367 if (this.labelAlign == 'left' ) {
7368 cfg.cls += ' form-horizontal';
7374 initEvents : function()
7376 this.el.on('submit', this.onSubmit, this);
7377 // this was added as random key presses on the form where triggering form submit.
7378 this.el.on('keypress', function(e) {
7379 if (e.getCharCode() != 13) {
7382 // we might need to allow it for textareas.. and some other items.
7383 // check e.getTarget().
7385 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7389 Roo.log("keypress blocked");
7397 onSubmit : function(e){
7402 * Returns true if client-side validation on the form is successful.
7405 isValid : function(){
7406 var items = this.getItems();
7408 items.each(function(f){
7417 * Returns true if any fields in this form have changed since their original load.
7420 isDirty : function(){
7422 var items = this.getItems();
7423 items.each(function(f){
7433 * Performs a predefined action (submit or load) or custom actions you define on this form.
7434 * @param {String} actionName The name of the action type
7435 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7436 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7437 * accept other config options):
7439 Property Type Description
7440 ---------------- --------------- ----------------------------------------------------------------------------------
7441 url String The url for the action (defaults to the form's url)
7442 method String The form method to use (defaults to the form's method, or POST if not defined)
7443 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7444 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7445 validate the form on the client (defaults to false)
7447 * @return {BasicForm} this
7449 doAction : function(action, options){
7450 if(typeof action == 'string'){
7451 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7453 if(this.fireEvent('beforeaction', this, action) !== false){
7454 this.beforeAction(action);
7455 action.run.defer(100, action);
7461 beforeAction : function(action){
7462 var o = action.options;
7465 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7467 // not really supported yet.. ??
7469 //if(this.waitMsgTarget === true){
7470 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7471 //}else if(this.waitMsgTarget){
7472 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7473 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7475 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7481 afterAction : function(action, success){
7482 this.activeAction = null;
7483 var o = action.options;
7485 //if(this.waitMsgTarget === true){
7487 //}else if(this.waitMsgTarget){
7488 // this.waitMsgTarget.unmask();
7490 // Roo.MessageBox.updateProgress(1);
7491 // Roo.MessageBox.hide();
7498 Roo.callback(o.success, o.scope, [this, action]);
7499 this.fireEvent('actioncomplete', this, action);
7503 // failure condition..
7504 // we have a scenario where updates need confirming.
7505 // eg. if a locking scenario exists..
7506 // we look for { errors : { needs_confirm : true }} in the response.
7508 (typeof(action.result) != 'undefined') &&
7509 (typeof(action.result.errors) != 'undefined') &&
7510 (typeof(action.result.errors.needs_confirm) != 'undefined')
7513 Roo.log("not supported yet");
7516 Roo.MessageBox.confirm(
7517 "Change requires confirmation",
7518 action.result.errorMsg,
7523 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7533 Roo.callback(o.failure, o.scope, [this, action]);
7534 // show an error message if no failed handler is set..
7535 if (!this.hasListener('actionfailed')) {
7536 Roo.log("need to add dialog support");
7538 Roo.MessageBox.alert("Error",
7539 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7540 action.result.errorMsg :
7541 "Saving Failed, please check your entries or try again"
7546 this.fireEvent('actionfailed', this, action);
7551 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7552 * @param {String} id The value to search for
7555 findField : function(id){
7556 var items = this.getItems();
7557 var field = items.get(id);
7559 items.each(function(f){
7560 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7567 return field || null;
7570 * Mark fields in this form invalid in bulk.
7571 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7572 * @return {BasicForm} this
7574 markInvalid : function(errors){
7575 if(errors instanceof Array){
7576 for(var i = 0, len = errors.length; i < len; i++){
7577 var fieldError = errors[i];
7578 var f = this.findField(fieldError.id);
7580 f.markInvalid(fieldError.msg);
7586 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7587 field.markInvalid(errors[id]);
7591 //Roo.each(this.childForms || [], function (f) {
7592 // f.markInvalid(errors);
7599 * Set values for fields in this form in bulk.
7600 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7601 * @return {BasicForm} this
7603 setValues : function(values){
7604 if(values instanceof Array){ // array of objects
7605 for(var i = 0, len = values.length; i < len; i++){
7607 var f = this.findField(v.id);
7609 f.setValue(v.value);
7610 if(this.trackResetOnLoad){
7611 f.originalValue = f.getValue();
7615 }else{ // object hash
7618 if(typeof values[id] != 'function' && (field = this.findField(id))){
7620 if (field.setFromData &&
7622 field.displayField &&
7623 // combos' with local stores can
7624 // be queried via setValue()
7625 // to set their value..
7626 (field.store && !field.store.isLocal)
7630 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7631 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7632 field.setFromData(sd);
7635 field.setValue(values[id]);
7639 if(this.trackResetOnLoad){
7640 field.originalValue = field.getValue();
7646 //Roo.each(this.childForms || [], function (f) {
7647 // f.setValues(values);
7654 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7655 * they are returned as an array.
7656 * @param {Boolean} asString
7659 getValues : function(asString){
7660 //if (this.childForms) {
7661 // copy values from the child forms
7662 // Roo.each(this.childForms, function (f) {
7663 // this.setValues(f.getValues());
7669 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7670 if(asString === true){
7673 return Roo.urlDecode(fs);
7677 * Returns the fields in this form as an object with key/value pairs.
7678 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7681 getFieldValues : function(with_hidden)
7683 var items = this.getItems();
7685 items.each(function(f){
7689 var v = f.getValue();
7690 if (f.inputType =='radio') {
7691 if (typeof(ret[f.getName()]) == 'undefined') {
7692 ret[f.getName()] = ''; // empty..
7695 if (!f.el.dom.checked) {
7703 // not sure if this supported any more..
7704 if ((typeof(v) == 'object') && f.getRawValue) {
7705 v = f.getRawValue() ; // dates..
7707 // combo boxes where name != hiddenName...
7708 if (f.name != f.getName()) {
7709 ret[f.name] = f.getRawValue();
7711 ret[f.getName()] = v;
7718 * Clears all invalid messages in this form.
7719 * @return {BasicForm} this
7721 clearInvalid : function(){
7722 var items = this.getItems();
7724 items.each(function(f){
7735 * @return {BasicForm} this
7738 var items = this.getItems();
7739 items.each(function(f){
7743 Roo.each(this.childForms || [], function (f) {
7750 getItems : function()
7752 var r=new Roo.util.MixedCollection(false, function(o){
7753 return o.id || (o.id = Roo.id());
7755 var iter = function(el) {
7762 Roo.each(el.items,function(e) {
7782 * Ext JS Library 1.1.1
7783 * Copyright(c) 2006-2007, Ext JS, LLC.
7785 * Originally Released Under LGPL - original licence link has changed is not relivant.
7788 * <script type="text/javascript">
7791 * @class Roo.form.VTypes
7792 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7795 Roo.form.VTypes = function(){
7796 // closure these in so they are only created once.
7797 var alpha = /^[a-zA-Z_]+$/;
7798 var alphanum = /^[a-zA-Z0-9_]+$/;
7799 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7800 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7802 // All these messages and functions are configurable
7805 * The function used to validate email addresses
7806 * @param {String} value The email address
7808 'email' : function(v){
7809 return email.test(v);
7812 * The error text to display when the email validation function returns false
7815 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7817 * The keystroke filter mask to be applied on email input
7820 'emailMask' : /[a-z0-9_\.\-@]/i,
7823 * The function used to validate URLs
7824 * @param {String} value The URL
7826 'url' : function(v){
7830 * The error text to display when the url validation function returns false
7833 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7836 * The function used to validate alpha values
7837 * @param {String} value The value
7839 'alpha' : function(v){
7840 return alpha.test(v);
7843 * The error text to display when the alpha validation function returns false
7846 'alphaText' : 'This field should only contain letters and _',
7848 * The keystroke filter mask to be applied on alpha input
7851 'alphaMask' : /[a-z_]/i,
7854 * The function used to validate alphanumeric values
7855 * @param {String} value The value
7857 'alphanum' : function(v){
7858 return alphanum.test(v);
7861 * The error text to display when the alphanumeric validation function returns false
7864 'alphanumText' : 'This field should only contain letters, numbers and _',
7866 * The keystroke filter mask to be applied on alphanumeric input
7869 'alphanumMask' : /[a-z0-9_]/i
7879 * @class Roo.bootstrap.Input
7880 * @extends Roo.bootstrap.Component
7881 * Bootstrap Input class
7882 * @cfg {Boolean} disabled is it disabled
7883 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7884 * @cfg {String} name name of the input
7885 * @cfg {string} fieldLabel - the label associated
7886 * @cfg {string} placeholder - placeholder to put in text.
7887 * @cfg {string} before - input group add on before
7888 * @cfg {string} after - input group add on after
7889 * @cfg {string} size - (lg|sm) or leave empty..
7890 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7891 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7892 * @cfg {Number} md colspan out of 12 for computer-sized screens
7893 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7894 * @cfg {string} value default value of the input
7895 * @cfg {Number} labelWidth set the width of label (0-12)
7896 * @cfg {String} labelAlign (top|left)
7897 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7898 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7900 * @cfg {String} align (left|center|right) Default left
7901 * @cfg {Boolean} forceFeedback (true|false) Default false
7907 * Create a new Input
7908 * @param {Object} config The config object
7911 Roo.bootstrap.Input = function(config){
7912 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7917 * Fires when this field receives input focus.
7918 * @param {Roo.form.Field} this
7923 * Fires when this field loses input focus.
7924 * @param {Roo.form.Field} this
7929 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7930 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7931 * @param {Roo.form.Field} this
7932 * @param {Roo.EventObject} e The event object
7937 * Fires just before the field blurs if the field value has changed.
7938 * @param {Roo.form.Field} this
7939 * @param {Mixed} newValue The new value
7940 * @param {Mixed} oldValue The original value
7945 * Fires after the field has been marked as invalid.
7946 * @param {Roo.form.Field} this
7947 * @param {String} msg The validation message
7952 * Fires after the field has been validated with no errors.
7953 * @param {Roo.form.Field} this
7958 * Fires after the key up
7959 * @param {Roo.form.Field} this
7960 * @param {Roo.EventObject} e The event Object
7966 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7968 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7969 automatic validation (defaults to "keyup").
7971 validationEvent : "keyup",
7973 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7975 validateOnBlur : true,
7977 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7979 validationDelay : 250,
7981 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7983 focusClass : "x-form-focus", // not needed???
7987 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7989 invalidClass : "has-warning",
7992 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7994 validClass : "has-success",
7997 * @cfg {Boolean} hasFeedback (true|false) default true
8002 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8004 invalidFeedbackClass : "glyphicon-warning-sign",
8007 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8009 validFeedbackClass : "glyphicon-ok",
8012 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8014 selectOnFocus : false,
8017 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8021 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8026 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8028 disableKeyFilter : false,
8031 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8035 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8039 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8041 blankText : "This field is required",
8044 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8048 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8050 maxLength : Number.MAX_VALUE,
8052 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8054 minLengthText : "The minimum length for this field is {0}",
8056 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8058 maxLengthText : "The maximum length for this field is {0}",
8062 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8063 * If available, this function will be called only after the basic validators all return true, and will be passed the
8064 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8068 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8069 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8070 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8074 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8078 autocomplete: false,
8097 formatedValue : false,
8098 forceFeedback : false,
8100 parentLabelAlign : function()
8103 while (parent.parent()) {
8104 parent = parent.parent();
8105 if (typeof(parent.labelAlign) !='undefined') {
8106 return parent.labelAlign;
8113 getAutoCreate : function(){
8115 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8121 if(this.inputType != 'hidden'){
8122 cfg.cls = 'form-group' //input-group
8128 type : this.inputType,
8130 cls : 'form-control',
8131 placeholder : this.placeholder || '',
8132 autocomplete : this.autocomplete || 'new-password'
8137 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8140 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8141 input.maxLength = this.maxLength;
8144 if (this.disabled) {
8145 input.disabled=true;
8148 if (this.readOnly) {
8149 input.readonly=true;
8153 input.name = this.name;
8156 input.cls += ' input-' + this.size;
8159 ['xs','sm','md','lg'].map(function(size){
8160 if (settings[size]) {
8161 cfg.cls += ' col-' + size + '-' + settings[size];
8165 var inputblock = input;
8169 cls: 'glyphicon form-control-feedback'
8172 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8175 cls : 'has-feedback',
8183 if (this.before || this.after) {
8186 cls : 'input-group',
8190 if (this.before && typeof(this.before) == 'string') {
8192 inputblock.cn.push({
8194 cls : 'roo-input-before input-group-addon',
8198 if (this.before && typeof(this.before) == 'object') {
8199 this.before = Roo.factory(this.before);
8201 inputblock.cn.push({
8203 cls : 'roo-input-before input-group-' +
8204 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8208 inputblock.cn.push(input);
8210 if (this.after && typeof(this.after) == 'string') {
8211 inputblock.cn.push({
8213 cls : 'roo-input-after input-group-addon',
8217 if (this.after && typeof(this.after) == 'object') {
8218 this.after = Roo.factory(this.after);
8220 inputblock.cn.push({
8222 cls : 'roo-input-after input-group-' +
8223 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8227 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8228 inputblock.cls += ' has-feedback';
8229 inputblock.cn.push(feedback);
8233 if (align ==='left' && this.fieldLabel.length) {
8240 cls : 'control-label col-sm-' + this.labelWidth,
8241 html : this.fieldLabel
8245 cls : "col-sm-" + (12 - this.labelWidth),
8252 } else if ( this.fieldLabel.length) {
8258 //cls : 'input-group-addon',
8259 html : this.fieldLabel
8278 if (this.parentType === 'Navbar' && this.parent().bar) {
8279 cfg.cls += ' navbar-form';
8286 * return the real input element.
8288 inputEl: function ()
8290 return this.el.select('input.form-control',true).first();
8293 tooltipEl : function()
8295 return this.inputEl();
8298 setDisabled : function(v)
8300 var i = this.inputEl().dom;
8302 i.removeAttribute('disabled');
8306 i.setAttribute('disabled','true');
8308 initEvents : function()
8311 this.inputEl().on("keydown" , this.fireKey, this);
8312 this.inputEl().on("focus", this.onFocus, this);
8313 this.inputEl().on("blur", this.onBlur, this);
8315 this.inputEl().relayEvent('keyup', this);
8317 // reference to original value for reset
8318 this.originalValue = this.getValue();
8319 //Roo.form.TextField.superclass.initEvents.call(this);
8320 if(this.validationEvent == 'keyup'){
8321 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8322 this.inputEl().on('keyup', this.filterValidation, this);
8324 else if(this.validationEvent !== false){
8325 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8328 if(this.selectOnFocus){
8329 this.on("focus", this.preFocus, this);
8332 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8333 this.inputEl().on("keypress", this.filterKeys, this);
8336 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8337 this.el.on("click", this.autoSize, this);
8340 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8341 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8344 if (typeof(this.before) == 'object') {
8345 this.before.render(this.el.select('.roo-input-before',true).first());
8347 if (typeof(this.after) == 'object') {
8348 this.after.render(this.el.select('.roo-input-after',true).first());
8353 filterValidation : function(e){
8354 if(!e.isNavKeyPress()){
8355 this.validationTask.delay(this.validationDelay);
8359 * Validates the field value
8360 * @return {Boolean} True if the value is valid, else false
8362 validate : function(){
8363 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8364 if(this.disabled || this.validateValue(this.getRawValue())){
8375 * Validates a value according to the field's validation rules and marks the field as invalid
8376 * if the validation fails
8377 * @param {Mixed} value The value to validate
8378 * @return {Boolean} True if the value is valid, else false
8380 validateValue : function(value){
8381 if(value.length < 1) { // if it's blank
8382 if(this.allowBlank){
8388 if(value.length < this.minLength){
8391 if(value.length > this.maxLength){
8395 var vt = Roo.form.VTypes;
8396 if(!vt[this.vtype](value, this)){
8400 if(typeof this.validator == "function"){
8401 var msg = this.validator(value);
8407 if(this.regex && !this.regex.test(value)){
8417 fireKey : function(e){
8418 //Roo.log('field ' + e.getKey());
8419 if(e.isNavKeyPress()){
8420 this.fireEvent("specialkey", this, e);
8423 focus : function (selectText){
8425 this.inputEl().focus();
8426 if(selectText === true){
8427 this.inputEl().dom.select();
8433 onFocus : function(){
8434 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8435 // this.el.addClass(this.focusClass);
8438 this.hasFocus = true;
8439 this.startValue = this.getValue();
8440 this.fireEvent("focus", this);
8444 beforeBlur : Roo.emptyFn,
8448 onBlur : function(){
8450 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8451 //this.el.removeClass(this.focusClass);
8453 this.hasFocus = false;
8454 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8457 var v = this.getValue();
8458 if(String(v) !== String(this.startValue)){
8459 this.fireEvent('change', this, v, this.startValue);
8461 this.fireEvent("blur", this);
8465 * Resets the current field value to the originally loaded value and clears any validation messages
8468 this.setValue(this.originalValue);
8472 * Returns the name of the field
8473 * @return {Mixed} name The name field
8475 getName: function(){
8479 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8480 * @return {Mixed} value The field value
8482 getValue : function(){
8484 var v = this.inputEl().getValue();
8489 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8490 * @return {Mixed} value The field value
8492 getRawValue : function(){
8493 var v = this.inputEl().getValue();
8499 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8500 * @param {Mixed} value The value to set
8502 setRawValue : function(v){
8503 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8506 selectText : function(start, end){
8507 var v = this.getRawValue();
8509 start = start === undefined ? 0 : start;
8510 end = end === undefined ? v.length : end;
8511 var d = this.inputEl().dom;
8512 if(d.setSelectionRange){
8513 d.setSelectionRange(start, end);
8514 }else if(d.createTextRange){
8515 var range = d.createTextRange();
8516 range.moveStart("character", start);
8517 range.moveEnd("character", v.length-end);
8524 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8525 * @param {Mixed} value The value to set
8527 setValue : function(v){
8530 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8536 processValue : function(value){
8537 if(this.stripCharsRe){
8538 var newValue = value.replace(this.stripCharsRe, '');
8539 if(newValue !== value){
8540 this.setRawValue(newValue);
8547 preFocus : function(){
8549 if(this.selectOnFocus){
8550 this.inputEl().dom.select();
8553 filterKeys : function(e){
8555 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8558 var c = e.getCharCode(), cc = String.fromCharCode(c);
8559 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8562 if(!this.maskRe.test(cc)){
8567 * Clear any invalid styles/messages for this field
8569 clearInvalid : function(){
8571 if(!this.el || this.preventMark){ // not rendered
8575 var label = this.el.select('label', true).first();
8576 var icon = this.el.select('i.fa-star', true).first();
8582 this.el.removeClass(this.invalidClass);
8584 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8586 var feedback = this.el.select('.form-control-feedback', true).first();
8589 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8594 this.fireEvent('valid', this);
8598 * Mark this field as valid
8600 markValid : function()
8602 if(!this.el || this.preventMark){ // not rendered
8606 this.el.removeClass([this.invalidClass, this.validClass]);
8608 var feedback = this.el.select('.form-control-feedback', true).first();
8611 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8614 if(this.disabled || this.allowBlank){
8618 var formGroup = this.el.findParent('.form-group', false, true);
8622 var label = formGroup.select('label', true).first();
8623 var icon = formGroup.select('i.fa-star', true).first();
8630 this.el.addClass(this.validClass);
8632 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8634 var feedback = this.el.select('.form-control-feedback', true).first();
8637 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8638 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8643 this.fireEvent('valid', this);
8647 * Mark this field as invalid
8648 * @param {String} msg The validation message
8650 markInvalid : function(msg)
8652 if(!this.el || this.preventMark){ // not rendered
8656 this.el.removeClass([this.invalidClass, this.validClass]);
8658 var feedback = this.el.select('.form-control-feedback', true).first();
8661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8664 if(this.disabled || this.allowBlank){
8668 var formGroup = this.el.findParent('.form-group', false, true);
8671 var label = formGroup.select('label', true).first();
8672 var icon = formGroup.select('i.fa-star', true).first();
8674 if(!this.getValue().length && label && !icon){
8675 this.el.findParent('.form-group', false, true).createChild({
8677 cls : 'text-danger fa fa-lg fa-star',
8678 tooltip : 'This field is required',
8679 style : 'margin-right:5px;'
8685 this.el.addClass(this.invalidClass);
8687 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8689 var feedback = this.el.select('.form-control-feedback', true).first();
8692 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8694 if(this.getValue().length || this.forceFeedback){
8695 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8702 this.fireEvent('invalid', this, msg);
8705 SafariOnKeyDown : function(event)
8707 // this is a workaround for a password hang bug on chrome/ webkit.
8709 var isSelectAll = false;
8711 if(this.inputEl().dom.selectionEnd > 0){
8712 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8714 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8715 event.preventDefault();
8720 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8722 event.preventDefault();
8723 // this is very hacky as keydown always get's upper case.
8725 var cc = String.fromCharCode(event.getCharCode());
8726 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8730 adjustWidth : function(tag, w){
8731 tag = tag.toLowerCase();
8732 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8733 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8737 if(tag == 'textarea'){
8740 }else if(Roo.isOpera){
8744 if(tag == 'textarea'){
8763 * @class Roo.bootstrap.TextArea
8764 * @extends Roo.bootstrap.Input
8765 * Bootstrap TextArea class
8766 * @cfg {Number} cols Specifies the visible width of a text area
8767 * @cfg {Number} rows Specifies the visible number of lines in a text area
8768 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8769 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8770 * @cfg {string} html text
8773 * Create a new TextArea
8774 * @param {Object} config The config object
8777 Roo.bootstrap.TextArea = function(config){
8778 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8792 getAutoCreate : function(){
8794 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8805 value : this.value || '',
8806 html: this.html || '',
8807 cls : 'form-control',
8808 placeholder : this.placeholder || ''
8812 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8813 input.maxLength = this.maxLength;
8817 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8821 input.cols = this.cols;
8824 if (this.readOnly) {
8825 input.readonly = true;
8829 input.name = this.name;
8833 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8837 ['xs','sm','md','lg'].map(function(size){
8838 if (settings[size]) {
8839 cfg.cls += ' col-' + size + '-' + settings[size];
8843 var inputblock = input;
8845 if(this.hasFeedback && !this.allowBlank){
8849 cls: 'glyphicon form-control-feedback'
8853 cls : 'has-feedback',
8862 if (this.before || this.after) {
8865 cls : 'input-group',
8869 inputblock.cn.push({
8871 cls : 'input-group-addon',
8876 inputblock.cn.push(input);
8878 if(this.hasFeedback && !this.allowBlank){
8879 inputblock.cls += ' has-feedback';
8880 inputblock.cn.push(feedback);
8884 inputblock.cn.push({
8886 cls : 'input-group-addon',
8893 if (align ==='left' && this.fieldLabel.length) {
8894 // Roo.log("left and has label");
8900 cls : 'control-label col-sm-' + this.labelWidth,
8901 html : this.fieldLabel
8905 cls : "col-sm-" + (12 - this.labelWidth),
8912 } else if ( this.fieldLabel.length) {
8913 // Roo.log(" label");
8918 //cls : 'input-group-addon',
8919 html : this.fieldLabel
8929 // Roo.log(" no label && no align");
8939 if (this.disabled) {
8940 input.disabled=true;
8947 * return the real textarea element.
8949 inputEl: function ()
8951 return this.el.select('textarea.form-control',true).first();
8955 * Clear any invalid styles/messages for this field
8957 clearInvalid : function()
8960 if(!this.el || this.preventMark){ // not rendered
8964 var label = this.el.select('label', true).first();
8965 var icon = this.el.select('i.fa-star', true).first();
8971 this.el.removeClass(this.invalidClass);
8973 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8975 var feedback = this.el.select('.form-control-feedback', true).first();
8978 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8983 this.fireEvent('valid', this);
8987 * Mark this field as valid
8989 markValid : function()
8991 if(!this.el || this.preventMark){ // not rendered
8995 this.el.removeClass([this.invalidClass, this.validClass]);
8997 var feedback = this.el.select('.form-control-feedback', true).first();
9000 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9003 if(this.disabled || this.allowBlank){
9007 var label = this.el.select('label', true).first();
9008 var icon = this.el.select('i.fa-star', true).first();
9014 this.el.addClass(this.validClass);
9016 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9018 var feedback = this.el.select('.form-control-feedback', true).first();
9021 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9022 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9027 this.fireEvent('valid', this);
9031 * Mark this field as invalid
9032 * @param {String} msg The validation message
9034 markInvalid : function(msg)
9036 if(!this.el || this.preventMark){ // not rendered
9040 this.el.removeClass([this.invalidClass, this.validClass]);
9042 var feedback = this.el.select('.form-control-feedback', true).first();
9045 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9048 if(this.disabled || this.allowBlank){
9052 var label = this.el.select('label', true).first();
9053 var icon = this.el.select('i.fa-star', true).first();
9055 if(!this.getValue().length && label && !icon){
9056 this.el.createChild({
9058 cls : 'text-danger fa fa-lg fa-star',
9059 tooltip : 'This field is required',
9060 style : 'margin-right:5px;'
9064 this.el.addClass(this.invalidClass);
9066 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9068 var feedback = this.el.select('.form-control-feedback', true).first();
9071 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9073 if(this.getValue().length || this.forceFeedback){
9074 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9081 this.fireEvent('invalid', this, msg);
9089 * trigger field - base class for combo..
9094 * @class Roo.bootstrap.TriggerField
9095 * @extends Roo.bootstrap.Input
9096 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9097 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9098 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9099 * for which you can provide a custom implementation. For example:
9101 var trigger = new Roo.bootstrap.TriggerField();
9102 trigger.onTriggerClick = myTriggerFn;
9103 trigger.applyTo('my-field');
9106 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9107 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9108 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9109 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9110 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9113 * Create a new TriggerField.
9114 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9115 * to the base TextField)
9117 Roo.bootstrap.TriggerField = function(config){
9118 this.mimicing = false;
9119 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9122 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9124 * @cfg {String} triggerClass A CSS class to apply to the trigger
9127 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9132 * @cfg {Boolean} removable (true|false) special filter default false
9136 /** @cfg {Boolean} grow @hide */
9137 /** @cfg {Number} growMin @hide */
9138 /** @cfg {Number} growMax @hide */
9144 autoSize: Roo.emptyFn,
9151 actionMode : 'wrap',
9156 getAutoCreate : function(){
9158 var align = this.labelAlign || this.parentLabelAlign();
9163 cls: 'form-group' //input-group
9170 type : this.inputType,
9171 cls : 'form-control',
9172 autocomplete: 'new-password',
9173 placeholder : this.placeholder || ''
9177 input.name = this.name;
9180 input.cls += ' input-' + this.size;
9183 if (this.disabled) {
9184 input.disabled=true;
9187 var inputblock = input;
9189 if(this.hasFeedback && !this.allowBlank){
9193 cls: 'glyphicon form-control-feedback'
9196 if(this.removable && !this.editable && !this.tickable){
9198 cls : 'has-feedback',
9204 cls : 'roo-combo-removable-btn close'
9211 cls : 'has-feedback',
9220 if(this.removable && !this.editable && !this.tickable){
9222 cls : 'roo-removable',
9228 cls : 'roo-combo-removable-btn close'
9235 if (this.before || this.after) {
9238 cls : 'input-group',
9242 inputblock.cn.push({
9244 cls : 'input-group-addon',
9249 inputblock.cn.push(input);
9251 if(this.hasFeedback && !this.allowBlank){
9252 inputblock.cls += ' has-feedback';
9253 inputblock.cn.push(feedback);
9257 inputblock.cn.push({
9259 cls : 'input-group-addon',
9272 cls: 'form-hidden-field'
9286 cls: 'form-hidden-field'
9290 cls: 'roo-select2-choices',
9294 cls: 'roo-select2-search-field',
9307 cls: 'roo-select2-container input-group',
9312 // cls: 'typeahead typeahead-long dropdown-menu',
9313 // style: 'display:none'
9318 if(!this.multiple && this.showToggleBtn){
9324 if (this.caret != false) {
9327 cls: 'fa fa-' + this.caret
9334 cls : 'input-group-addon btn dropdown-toggle',
9339 cls: 'combobox-clear',
9353 combobox.cls += ' roo-select2-container-multi';
9356 if (align ==='left' && this.fieldLabel.length) {
9358 // Roo.log("left and has label");
9364 cls : 'control-label col-sm-' + this.labelWidth,
9365 html : this.fieldLabel
9369 cls : "col-sm-" + (12 - this.labelWidth),
9376 } else if ( this.fieldLabel.length) {
9377 // Roo.log(" label");
9382 //cls : 'input-group-addon',
9383 html : this.fieldLabel
9393 // Roo.log(" no label && no align");
9400 ['xs','sm','md','lg'].map(function(size){
9401 if (settings[size]) {
9402 cfg.cls += ' col-' + size + '-' + settings[size];
9413 onResize : function(w, h){
9414 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9415 // if(typeof w == 'number'){
9416 // var x = w - this.trigger.getWidth();
9417 // this.inputEl().setWidth(this.adjustWidth('input', x));
9418 // this.trigger.setStyle('left', x+'px');
9423 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9426 getResizeEl : function(){
9427 return this.inputEl();
9431 getPositionEl : function(){
9432 return this.inputEl();
9436 alignErrorIcon : function(){
9437 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9441 initEvents : function(){
9445 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9446 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9447 if(!this.multiple && this.showToggleBtn){
9448 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9449 if(this.hideTrigger){
9450 this.trigger.setDisplayed(false);
9452 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9456 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9459 if(this.removable && !this.editable && !this.tickable){
9460 var close = this.closeTriggerEl();
9463 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9464 close.on('click', this.removeBtnClick, this, close);
9468 //this.trigger.addClassOnOver('x-form-trigger-over');
9469 //this.trigger.addClassOnClick('x-form-trigger-click');
9472 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9476 closeTriggerEl : function()
9478 var close = this.el.select('.roo-combo-removable-btn', true).first();
9479 return close ? close : false;
9482 removeBtnClick : function(e, h, el)
9486 if(this.fireEvent("remove", this) !== false){
9488 this.fireEvent("afterremove", this)
9492 createList : function()
9494 this.list = Roo.get(document.body).createChild({
9496 cls: 'typeahead typeahead-long dropdown-menu',
9497 style: 'display:none'
9500 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9505 initTrigger : function(){
9510 onDestroy : function(){
9512 this.trigger.removeAllListeners();
9513 // this.trigger.remove();
9516 // this.wrap.remove();
9518 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9522 onFocus : function(){
9523 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9526 this.wrap.addClass('x-trigger-wrap-focus');
9527 this.mimicing = true;
9528 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9529 if(this.monitorTab){
9530 this.el.on("keydown", this.checkTab, this);
9537 checkTab : function(e){
9538 if(e.getKey() == e.TAB){
9544 onBlur : function(){
9549 mimicBlur : function(e, t){
9551 if(!this.wrap.contains(t) && this.validateBlur()){
9558 triggerBlur : function(){
9559 this.mimicing = false;
9560 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9561 if(this.monitorTab){
9562 this.el.un("keydown", this.checkTab, this);
9564 //this.wrap.removeClass('x-trigger-wrap-focus');
9565 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9569 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9570 validateBlur : function(e, t){
9575 onDisable : function(){
9576 this.inputEl().dom.disabled = true;
9577 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9579 // this.wrap.addClass('x-item-disabled');
9584 onEnable : function(){
9585 this.inputEl().dom.disabled = false;
9586 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9588 // this.el.removeClass('x-item-disabled');
9593 onShow : function(){
9594 var ae = this.getActionEl();
9597 ae.dom.style.display = '';
9598 ae.dom.style.visibility = 'visible';
9604 onHide : function(){
9605 var ae = this.getActionEl();
9606 ae.dom.style.display = 'none';
9610 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9611 * by an implementing function.
9613 * @param {EventObject} e
9615 onTriggerClick : Roo.emptyFn
9619 * Ext JS Library 1.1.1
9620 * Copyright(c) 2006-2007, Ext JS, LLC.
9622 * Originally Released Under LGPL - original licence link has changed is not relivant.
9625 * <script type="text/javascript">
9630 * @class Roo.data.SortTypes
9632 * Defines the default sorting (casting?) comparison functions used when sorting data.
9634 Roo.data.SortTypes = {
9636 * Default sort that does nothing
9637 * @param {Mixed} s The value being converted
9638 * @return {Mixed} The comparison value
9645 * The regular expression used to strip tags
9649 stripTagsRE : /<\/?[^>]+>/gi,
9652 * Strips all HTML tags to sort on text only
9653 * @param {Mixed} s The value being converted
9654 * @return {String} The comparison value
9656 asText : function(s){
9657 return String(s).replace(this.stripTagsRE, "");
9661 * Strips all HTML tags to sort on text only - Case insensitive
9662 * @param {Mixed} s The value being converted
9663 * @return {String} The comparison value
9665 asUCText : function(s){
9666 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9670 * Case insensitive string
9671 * @param {Mixed} s The value being converted
9672 * @return {String} The comparison value
9674 asUCString : function(s) {
9675 return String(s).toUpperCase();
9680 * @param {Mixed} s The value being converted
9681 * @return {Number} The comparison value
9683 asDate : function(s) {
9687 if(s instanceof Date){
9690 return Date.parse(String(s));
9695 * @param {Mixed} s The value being converted
9696 * @return {Float} The comparison value
9698 asFloat : function(s) {
9699 var val = parseFloat(String(s).replace(/,/g, ""));
9708 * @param {Mixed} s The value being converted
9709 * @return {Number} The comparison value
9711 asInt : function(s) {
9712 var val = parseInt(String(s).replace(/,/g, ""));
9720 * Ext JS Library 1.1.1
9721 * Copyright(c) 2006-2007, Ext JS, LLC.
9723 * Originally Released Under LGPL - original licence link has changed is not relivant.
9726 * <script type="text/javascript">
9730 * @class Roo.data.Record
9731 * Instances of this class encapsulate both record <em>definition</em> information, and record
9732 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9733 * to access Records cached in an {@link Roo.data.Store} object.<br>
9735 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9736 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9739 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9741 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9742 * {@link #create}. The parameters are the same.
9743 * @param {Array} data An associative Array of data values keyed by the field name.
9744 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9745 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9746 * not specified an integer id is generated.
9748 Roo.data.Record = function(data, id){
9749 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9754 * Generate a constructor for a specific record layout.
9755 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9756 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9757 * Each field definition object may contain the following properties: <ul>
9758 * <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,
9759 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9760 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9761 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9762 * is being used, then this is a string containing the javascript expression to reference the data relative to
9763 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9764 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9765 * this may be omitted.</p></li>
9766 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9767 * <ul><li>auto (Default, implies no conversion)</li>
9772 * <li>date</li></ul></p></li>
9773 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9774 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9775 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9776 * by the Reader into an object that will be stored in the Record. It is passed the
9777 * following parameters:<ul>
9778 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9780 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9782 * <br>usage:<br><pre><code>
9783 var TopicRecord = Roo.data.Record.create(
9784 {name: 'title', mapping: 'topic_title'},
9785 {name: 'author', mapping: 'username'},
9786 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9787 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9788 {name: 'lastPoster', mapping: 'user2'},
9789 {name: 'excerpt', mapping: 'post_text'}
9792 var myNewRecord = new TopicRecord({
9793 title: 'Do my job please',
9796 lastPost: new Date(),
9797 lastPoster: 'Animal',
9798 excerpt: 'No way dude!'
9800 myStore.add(myNewRecord);
9805 Roo.data.Record.create = function(o){
9807 f.superclass.constructor.apply(this, arguments);
9809 Roo.extend(f, Roo.data.Record);
9810 var p = f.prototype;
9811 p.fields = new Roo.util.MixedCollection(false, function(field){
9814 for(var i = 0, len = o.length; i < len; i++){
9815 p.fields.add(new Roo.data.Field(o[i]));
9817 f.getField = function(name){
9818 return p.fields.get(name);
9823 Roo.data.Record.AUTO_ID = 1000;
9824 Roo.data.Record.EDIT = 'edit';
9825 Roo.data.Record.REJECT = 'reject';
9826 Roo.data.Record.COMMIT = 'commit';
9828 Roo.data.Record.prototype = {
9830 * Readonly flag - true if this record has been modified.
9839 join : function(store){
9844 * Set the named field to the specified value.
9845 * @param {String} name The name of the field to set.
9846 * @param {Object} value The value to set the field to.
9848 set : function(name, value){
9849 if(this.data[name] == value){
9856 if(typeof this.modified[name] == 'undefined'){
9857 this.modified[name] = this.data[name];
9859 this.data[name] = value;
9860 if(!this.editing && this.store){
9861 this.store.afterEdit(this);
9866 * Get the value of the named field.
9867 * @param {String} name The name of the field to get the value of.
9868 * @return {Object} The value of the field.
9870 get : function(name){
9871 return this.data[name];
9875 beginEdit : function(){
9876 this.editing = true;
9881 cancelEdit : function(){
9882 this.editing = false;
9883 delete this.modified;
9887 endEdit : function(){
9888 this.editing = false;
9889 if(this.dirty && this.store){
9890 this.store.afterEdit(this);
9895 * Usually called by the {@link Roo.data.Store} which owns the Record.
9896 * Rejects all changes made to the Record since either creation, or the last commit operation.
9897 * Modified fields are reverted to their original values.
9899 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9900 * of reject operations.
9902 reject : function(){
9903 var m = this.modified;
9905 if(typeof m[n] != "function"){
9906 this.data[n] = m[n];
9910 delete this.modified;
9911 this.editing = false;
9913 this.store.afterReject(this);
9918 * Usually called by the {@link Roo.data.Store} which owns the Record.
9919 * Commits all changes made to the Record since either creation, or the last commit operation.
9921 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9922 * of commit operations.
9924 commit : function(){
9926 delete this.modified;
9927 this.editing = false;
9929 this.store.afterCommit(this);
9934 hasError : function(){
9935 return this.error != null;
9939 clearError : function(){
9944 * Creates a copy of this record.
9945 * @param {String} id (optional) A new record id if you don't want to use this record's id
9948 copy : function(newId) {
9949 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9953 * Ext JS Library 1.1.1
9954 * Copyright(c) 2006-2007, Ext JS, LLC.
9956 * Originally Released Under LGPL - original licence link has changed is not relivant.
9959 * <script type="text/javascript">
9965 * @class Roo.data.Store
9966 * @extends Roo.util.Observable
9967 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9968 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9970 * 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
9971 * has no knowledge of the format of the data returned by the Proxy.<br>
9973 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9974 * instances from the data object. These records are cached and made available through accessor functions.
9976 * Creates a new Store.
9977 * @param {Object} config A config object containing the objects needed for the Store to access data,
9978 * and read the data into Records.
9980 Roo.data.Store = function(config){
9981 this.data = new Roo.util.MixedCollection(false);
9982 this.data.getKey = function(o){
9985 this.baseParams = {};
9992 "multisort" : "_multisort"
9995 if(config && config.data){
9996 this.inlineData = config.data;
10000 Roo.apply(this, config);
10002 if(this.reader){ // reader passed
10003 this.reader = Roo.factory(this.reader, Roo.data);
10004 this.reader.xmodule = this.xmodule || false;
10005 if(!this.recordType){
10006 this.recordType = this.reader.recordType;
10008 if(this.reader.onMetaChange){
10009 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10013 if(this.recordType){
10014 this.fields = this.recordType.prototype.fields;
10016 this.modified = [];
10020 * @event datachanged
10021 * Fires when the data cache has changed, and a widget which is using this Store
10022 * as a Record cache should refresh its view.
10023 * @param {Store} this
10025 datachanged : true,
10027 * @event metachange
10028 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10029 * @param {Store} this
10030 * @param {Object} meta The JSON metadata
10035 * Fires when Records have been added to the Store
10036 * @param {Store} this
10037 * @param {Roo.data.Record[]} records The array of Records added
10038 * @param {Number} index The index at which the record(s) were added
10043 * Fires when a Record has been removed from the Store
10044 * @param {Store} this
10045 * @param {Roo.data.Record} record The Record that was removed
10046 * @param {Number} index The index at which the record was removed
10051 * Fires when a Record has been updated
10052 * @param {Store} this
10053 * @param {Roo.data.Record} record The Record that was updated
10054 * @param {String} operation The update operation being performed. Value may be one of:
10056 Roo.data.Record.EDIT
10057 Roo.data.Record.REJECT
10058 Roo.data.Record.COMMIT
10064 * Fires when the data cache has been cleared.
10065 * @param {Store} this
10069 * @event beforeload
10070 * Fires before a request is made for a new data object. If the beforeload handler returns false
10071 * the load action will be canceled.
10072 * @param {Store} this
10073 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10077 * @event beforeloadadd
10078 * Fires after a new set of Records has been loaded.
10079 * @param {Store} this
10080 * @param {Roo.data.Record[]} records The Records that were loaded
10081 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10083 beforeloadadd : true,
10086 * Fires after a new set of Records has been loaded, before they are added to the store.
10087 * @param {Store} this
10088 * @param {Roo.data.Record[]} records The Records that were loaded
10089 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10090 * @params {Object} return from reader
10094 * @event loadexception
10095 * Fires if an exception occurs in the Proxy during loading.
10096 * Called with the signature of the Proxy's "loadexception" event.
10097 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10100 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10101 * @param {Object} load options
10102 * @param {Object} jsonData from your request (normally this contains the Exception)
10104 loadexception : true
10108 this.proxy = Roo.factory(this.proxy, Roo.data);
10109 this.proxy.xmodule = this.xmodule || false;
10110 this.relayEvents(this.proxy, ["loadexception"]);
10112 this.sortToggle = {};
10113 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10115 Roo.data.Store.superclass.constructor.call(this);
10117 if(this.inlineData){
10118 this.loadData(this.inlineData);
10119 delete this.inlineData;
10123 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10125 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10126 * without a remote query - used by combo/forms at present.
10130 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10133 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10136 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10137 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10140 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10141 * on any HTTP request
10144 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10147 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10151 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10152 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10154 remoteSort : false,
10157 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10158 * loaded or when a record is removed. (defaults to false).
10160 pruneModifiedRecords : false,
10163 lastOptions : null,
10166 * Add Records to the Store and fires the add event.
10167 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10169 add : function(records){
10170 records = [].concat(records);
10171 for(var i = 0, len = records.length; i < len; i++){
10172 records[i].join(this);
10174 var index = this.data.length;
10175 this.data.addAll(records);
10176 this.fireEvent("add", this, records, index);
10180 * Remove a Record from the Store and fires the remove event.
10181 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10183 remove : function(record){
10184 var index = this.data.indexOf(record);
10185 this.data.removeAt(index);
10186 if(this.pruneModifiedRecords){
10187 this.modified.remove(record);
10189 this.fireEvent("remove", this, record, index);
10193 * Remove all Records from the Store and fires the clear event.
10195 removeAll : function(){
10197 if(this.pruneModifiedRecords){
10198 this.modified = [];
10200 this.fireEvent("clear", this);
10204 * Inserts Records to the Store at the given index and fires the add event.
10205 * @param {Number} index The start index at which to insert the passed Records.
10206 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10208 insert : function(index, records){
10209 records = [].concat(records);
10210 for(var i = 0, len = records.length; i < len; i++){
10211 this.data.insert(index, records[i]);
10212 records[i].join(this);
10214 this.fireEvent("add", this, records, index);
10218 * Get the index within the cache of the passed Record.
10219 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10220 * @return {Number} The index of the passed Record. Returns -1 if not found.
10222 indexOf : function(record){
10223 return this.data.indexOf(record);
10227 * Get the index within the cache of the Record with the passed id.
10228 * @param {String} id The id of the Record to find.
10229 * @return {Number} The index of the Record. Returns -1 if not found.
10231 indexOfId : function(id){
10232 return this.data.indexOfKey(id);
10236 * Get the Record with the specified id.
10237 * @param {String} id The id of the Record to find.
10238 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10240 getById : function(id){
10241 return this.data.key(id);
10245 * Get the Record at the specified index.
10246 * @param {Number} index The index of the Record to find.
10247 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10249 getAt : function(index){
10250 return this.data.itemAt(index);
10254 * Returns a range of Records between specified indices.
10255 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10256 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10257 * @return {Roo.data.Record[]} An array of Records
10259 getRange : function(start, end){
10260 return this.data.getRange(start, end);
10264 storeOptions : function(o){
10265 o = Roo.apply({}, o);
10268 this.lastOptions = o;
10272 * Loads the Record cache from the configured Proxy using the configured Reader.
10274 * If using remote paging, then the first load call must specify the <em>start</em>
10275 * and <em>limit</em> properties in the options.params property to establish the initial
10276 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10278 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10279 * and this call will return before the new data has been loaded. Perform any post-processing
10280 * in a callback function, or in a "load" event handler.</strong>
10282 * @param {Object} options An object containing properties which control loading options:<ul>
10283 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10284 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10285 * passed the following arguments:<ul>
10286 * <li>r : Roo.data.Record[]</li>
10287 * <li>options: Options object from the load call</li>
10288 * <li>success: Boolean success indicator</li></ul></li>
10289 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10290 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10293 load : function(options){
10294 options = options || {};
10295 if(this.fireEvent("beforeload", this, options) !== false){
10296 this.storeOptions(options);
10297 var p = Roo.apply(options.params || {}, this.baseParams);
10298 // if meta was not loaded from remote source.. try requesting it.
10299 if (!this.reader.metaFromRemote) {
10300 p._requestMeta = 1;
10302 if(this.sortInfo && this.remoteSort){
10303 var pn = this.paramNames;
10304 p[pn["sort"]] = this.sortInfo.field;
10305 p[pn["dir"]] = this.sortInfo.direction;
10307 if (this.multiSort) {
10308 var pn = this.paramNames;
10309 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10312 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10317 * Reloads the Record cache from the configured Proxy using the configured Reader and
10318 * the options from the last load operation performed.
10319 * @param {Object} options (optional) An object containing properties which may override the options
10320 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10321 * the most recently used options are reused).
10323 reload : function(options){
10324 this.load(Roo.applyIf(options||{}, this.lastOptions));
10328 // Called as a callback by the Reader during a load operation.
10329 loadRecords : function(o, options, success){
10330 if(!o || success === false){
10331 if(success !== false){
10332 this.fireEvent("load", this, [], options, o);
10334 if(options.callback){
10335 options.callback.call(options.scope || this, [], options, false);
10339 // if data returned failure - throw an exception.
10340 if (o.success === false) {
10341 // show a message if no listener is registered.
10342 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10343 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10345 // loadmask wil be hooked into this..
10346 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10349 var r = o.records, t = o.totalRecords || r.length;
10351 this.fireEvent("beforeloadadd", this, r, options, o);
10353 if(!options || options.add !== true){
10354 if(this.pruneModifiedRecords){
10355 this.modified = [];
10357 for(var i = 0, len = r.length; i < len; i++){
10361 this.data = this.snapshot;
10362 delete this.snapshot;
10365 this.data.addAll(r);
10366 this.totalLength = t;
10368 this.fireEvent("datachanged", this);
10370 this.totalLength = Math.max(t, this.data.length+r.length);
10373 this.fireEvent("load", this, r, options, o);
10374 if(options.callback){
10375 options.callback.call(options.scope || this, r, options, true);
10381 * Loads data from a passed data block. A Reader which understands the format of the data
10382 * must have been configured in the constructor.
10383 * @param {Object} data The data block from which to read the Records. The format of the data expected
10384 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10385 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10387 loadData : function(o, append){
10388 var r = this.reader.readRecords(o);
10389 this.loadRecords(r, {add: append}, true);
10393 * Gets the number of cached records.
10395 * <em>If using paging, this may not be the total size of the dataset. If the data object
10396 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10397 * the data set size</em>
10399 getCount : function(){
10400 return this.data.length || 0;
10404 * Gets the total number of records in the dataset as returned by the server.
10406 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10407 * the dataset size</em>
10409 getTotalCount : function(){
10410 return this.totalLength || 0;
10414 * Returns the sort state of the Store as an object with two properties:
10416 field {String} The name of the field by which the Records are sorted
10417 direction {String} The sort order, "ASC" or "DESC"
10420 getSortState : function(){
10421 return this.sortInfo;
10425 applySort : function(){
10426 if(this.sortInfo && !this.remoteSort){
10427 var s = this.sortInfo, f = s.field;
10428 var st = this.fields.get(f).sortType;
10429 var fn = function(r1, r2){
10430 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10431 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10433 this.data.sort(s.direction, fn);
10434 if(this.snapshot && this.snapshot != this.data){
10435 this.snapshot.sort(s.direction, fn);
10441 * Sets the default sort column and order to be used by the next load operation.
10442 * @param {String} fieldName The name of the field to sort by.
10443 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10445 setDefaultSort : function(field, dir){
10446 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10450 * Sort the Records.
10451 * If remote sorting is used, the sort is performed on the server, and the cache is
10452 * reloaded. If local sorting is used, the cache is sorted internally.
10453 * @param {String} fieldName The name of the field to sort by.
10454 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10456 sort : function(fieldName, dir){
10457 var f = this.fields.get(fieldName);
10459 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10461 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10462 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10467 this.sortToggle[f.name] = dir;
10468 this.sortInfo = {field: f.name, direction: dir};
10469 if(!this.remoteSort){
10471 this.fireEvent("datachanged", this);
10473 this.load(this.lastOptions);
10478 * Calls the specified function for each of the Records in the cache.
10479 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10480 * Returning <em>false</em> aborts and exits the iteration.
10481 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10483 each : function(fn, scope){
10484 this.data.each(fn, scope);
10488 * Gets all records modified since the last commit. Modified records are persisted across load operations
10489 * (e.g., during paging).
10490 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10492 getModifiedRecords : function(){
10493 return this.modified;
10497 createFilterFn : function(property, value, anyMatch){
10498 if(!value.exec){ // not a regex
10499 value = String(value);
10500 if(value.length == 0){
10503 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10505 return function(r){
10506 return value.test(r.data[property]);
10511 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10512 * @param {String} property A field on your records
10513 * @param {Number} start The record index to start at (defaults to 0)
10514 * @param {Number} end The last record index to include (defaults to length - 1)
10515 * @return {Number} The sum
10517 sum : function(property, start, end){
10518 var rs = this.data.items, v = 0;
10519 start = start || 0;
10520 end = (end || end === 0) ? end : rs.length-1;
10522 for(var i = start; i <= end; i++){
10523 v += (rs[i].data[property] || 0);
10529 * Filter the records by a specified property.
10530 * @param {String} field A field on your records
10531 * @param {String/RegExp} value Either a string that the field
10532 * should start with or a RegExp to test against the field
10533 * @param {Boolean} anyMatch True to match any part not just the beginning
10535 filter : function(property, value, anyMatch){
10536 var fn = this.createFilterFn(property, value, anyMatch);
10537 return fn ? this.filterBy(fn) : this.clearFilter();
10541 * Filter by a function. The specified function will be called with each
10542 * record in this data source. If the function returns true the record is included,
10543 * otherwise it is filtered.
10544 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10545 * @param {Object} scope (optional) The scope of the function (defaults to this)
10547 filterBy : function(fn, scope){
10548 this.snapshot = this.snapshot || this.data;
10549 this.data = this.queryBy(fn, scope||this);
10550 this.fireEvent("datachanged", this);
10554 * Query the records by a specified property.
10555 * @param {String} field A field on your records
10556 * @param {String/RegExp} value Either a string that the field
10557 * should start with or a RegExp to test against the field
10558 * @param {Boolean} anyMatch True to match any part not just the beginning
10559 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10561 query : function(property, value, anyMatch){
10562 var fn = this.createFilterFn(property, value, anyMatch);
10563 return fn ? this.queryBy(fn) : this.data.clone();
10567 * Query by a function. The specified function will be called with each
10568 * record in this data source. If the function returns true the record is included
10570 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10571 * @param {Object} scope (optional) The scope of the function (defaults to this)
10572 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10574 queryBy : function(fn, scope){
10575 var data = this.snapshot || this.data;
10576 return data.filterBy(fn, scope||this);
10580 * Collects unique values for a particular dataIndex from this store.
10581 * @param {String} dataIndex The property to collect
10582 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10583 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10584 * @return {Array} An array of the unique values
10586 collect : function(dataIndex, allowNull, bypassFilter){
10587 var d = (bypassFilter === true && this.snapshot) ?
10588 this.snapshot.items : this.data.items;
10589 var v, sv, r = [], l = {};
10590 for(var i = 0, len = d.length; i < len; i++){
10591 v = d[i].data[dataIndex];
10593 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10602 * Revert to a view of the Record cache with no filtering applied.
10603 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10605 clearFilter : function(suppressEvent){
10606 if(this.snapshot && this.snapshot != this.data){
10607 this.data = this.snapshot;
10608 delete this.snapshot;
10609 if(suppressEvent !== true){
10610 this.fireEvent("datachanged", this);
10616 afterEdit : function(record){
10617 if(this.modified.indexOf(record) == -1){
10618 this.modified.push(record);
10620 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10624 afterReject : function(record){
10625 this.modified.remove(record);
10626 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10630 afterCommit : function(record){
10631 this.modified.remove(record);
10632 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10636 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10637 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10639 commitChanges : function(){
10640 var m = this.modified.slice(0);
10641 this.modified = [];
10642 for(var i = 0, len = m.length; i < len; i++){
10648 * Cancel outstanding changes on all changed records.
10650 rejectChanges : function(){
10651 var m = this.modified.slice(0);
10652 this.modified = [];
10653 for(var i = 0, len = m.length; i < len; i++){
10658 onMetaChange : function(meta, rtype, o){
10659 this.recordType = rtype;
10660 this.fields = rtype.prototype.fields;
10661 delete this.snapshot;
10662 this.sortInfo = meta.sortInfo || this.sortInfo;
10663 this.modified = [];
10664 this.fireEvent('metachange', this, this.reader.meta);
10667 moveIndex : function(data, type)
10669 var index = this.indexOf(data);
10671 var newIndex = index + type;
10675 this.insert(newIndex, data);
10680 * Ext JS Library 1.1.1
10681 * Copyright(c) 2006-2007, Ext JS, LLC.
10683 * Originally Released Under LGPL - original licence link has changed is not relivant.
10686 * <script type="text/javascript">
10690 * @class Roo.data.SimpleStore
10691 * @extends Roo.data.Store
10692 * Small helper class to make creating Stores from Array data easier.
10693 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10694 * @cfg {Array} fields An array of field definition objects, or field name strings.
10695 * @cfg {Array} data The multi-dimensional array of data
10697 * @param {Object} config
10699 Roo.data.SimpleStore = function(config){
10700 Roo.data.SimpleStore.superclass.constructor.call(this, {
10702 reader: new Roo.data.ArrayReader({
10705 Roo.data.Record.create(config.fields)
10707 proxy : new Roo.data.MemoryProxy(config.data)
10711 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10713 * Ext JS Library 1.1.1
10714 * Copyright(c) 2006-2007, Ext JS, LLC.
10716 * Originally Released Under LGPL - original licence link has changed is not relivant.
10719 * <script type="text/javascript">
10724 * @extends Roo.data.Store
10725 * @class Roo.data.JsonStore
10726 * Small helper class to make creating Stores for JSON data easier. <br/>
10728 var store = new Roo.data.JsonStore({
10729 url: 'get-images.php',
10731 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10734 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10735 * JsonReader and HttpProxy (unless inline data is provided).</b>
10736 * @cfg {Array} fields An array of field definition objects, or field name strings.
10738 * @param {Object} config
10740 Roo.data.JsonStore = function(c){
10741 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10742 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10743 reader: new Roo.data.JsonReader(c, c.fields)
10746 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10748 * Ext JS Library 1.1.1
10749 * Copyright(c) 2006-2007, Ext JS, LLC.
10751 * Originally Released Under LGPL - original licence link has changed is not relivant.
10754 * <script type="text/javascript">
10758 Roo.data.Field = function(config){
10759 if(typeof config == "string"){
10760 config = {name: config};
10762 Roo.apply(this, config);
10765 this.type = "auto";
10768 var st = Roo.data.SortTypes;
10769 // named sortTypes are supported, here we look them up
10770 if(typeof this.sortType == "string"){
10771 this.sortType = st[this.sortType];
10774 // set default sortType for strings and dates
10775 if(!this.sortType){
10778 this.sortType = st.asUCString;
10781 this.sortType = st.asDate;
10784 this.sortType = st.none;
10789 var stripRe = /[\$,%]/g;
10791 // prebuilt conversion function for this field, instead of
10792 // switching every time we're reading a value
10794 var cv, dateFormat = this.dateFormat;
10799 cv = function(v){ return v; };
10802 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10806 return v !== undefined && v !== null && v !== '' ?
10807 parseInt(String(v).replace(stripRe, ""), 10) : '';
10812 return v !== undefined && v !== null && v !== '' ?
10813 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10818 cv = function(v){ return v === true || v === "true" || v == 1; };
10825 if(v instanceof Date){
10829 if(dateFormat == "timestamp"){
10830 return new Date(v*1000);
10832 return Date.parseDate(v, dateFormat);
10834 var parsed = Date.parse(v);
10835 return parsed ? new Date(parsed) : null;
10844 Roo.data.Field.prototype = {
10852 * Ext JS Library 1.1.1
10853 * Copyright(c) 2006-2007, Ext JS, LLC.
10855 * Originally Released Under LGPL - original licence link has changed is not relivant.
10858 * <script type="text/javascript">
10861 // Base class for reading structured data from a data source. This class is intended to be
10862 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10865 * @class Roo.data.DataReader
10866 * Base class for reading structured data from a data source. This class is intended to be
10867 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10870 Roo.data.DataReader = function(meta, recordType){
10874 this.recordType = recordType instanceof Array ?
10875 Roo.data.Record.create(recordType) : recordType;
10878 Roo.data.DataReader.prototype = {
10880 * Create an empty record
10881 * @param {Object} data (optional) - overlay some values
10882 * @return {Roo.data.Record} record created.
10884 newRow : function(d) {
10886 this.recordType.prototype.fields.each(function(c) {
10888 case 'int' : da[c.name] = 0; break;
10889 case 'date' : da[c.name] = new Date(); break;
10890 case 'float' : da[c.name] = 0.0; break;
10891 case 'boolean' : da[c.name] = false; break;
10892 default : da[c.name] = ""; break;
10896 return new this.recordType(Roo.apply(da, d));
10901 * Ext JS Library 1.1.1
10902 * Copyright(c) 2006-2007, Ext JS, LLC.
10904 * Originally Released Under LGPL - original licence link has changed is not relivant.
10907 * <script type="text/javascript">
10911 * @class Roo.data.DataProxy
10912 * @extends Roo.data.Observable
10913 * This class is an abstract base class for implementations which provide retrieval of
10914 * unformatted data objects.<br>
10916 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10917 * (of the appropriate type which knows how to parse the data object) to provide a block of
10918 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10920 * Custom implementations must implement the load method as described in
10921 * {@link Roo.data.HttpProxy#load}.
10923 Roo.data.DataProxy = function(){
10926 * @event beforeload
10927 * Fires before a network request is made to retrieve a data object.
10928 * @param {Object} This DataProxy object.
10929 * @param {Object} params The params parameter to the load function.
10934 * Fires before the load method's callback is called.
10935 * @param {Object} This DataProxy object.
10936 * @param {Object} o The data object.
10937 * @param {Object} arg The callback argument object passed to the load function.
10941 * @event loadexception
10942 * Fires if an Exception occurs during data retrieval.
10943 * @param {Object} This DataProxy object.
10944 * @param {Object} o The data object.
10945 * @param {Object} arg The callback argument object passed to the load function.
10946 * @param {Object} e The Exception.
10948 loadexception : true
10950 Roo.data.DataProxy.superclass.constructor.call(this);
10953 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10956 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10960 * Ext JS Library 1.1.1
10961 * Copyright(c) 2006-2007, Ext JS, LLC.
10963 * Originally Released Under LGPL - original licence link has changed is not relivant.
10966 * <script type="text/javascript">
10969 * @class Roo.data.MemoryProxy
10970 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10971 * to the Reader when its load method is called.
10973 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10975 Roo.data.MemoryProxy = function(data){
10979 Roo.data.MemoryProxy.superclass.constructor.call(this);
10983 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10985 * Load data from the requested source (in this case an in-memory
10986 * data object passed to the constructor), read the data object into
10987 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10988 * process that block using the passed callback.
10989 * @param {Object} params This parameter is not used by the MemoryProxy class.
10990 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10991 * object into a block of Roo.data.Records.
10992 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10993 * The function must be passed <ul>
10994 * <li>The Record block object</li>
10995 * <li>The "arg" argument from the load function</li>
10996 * <li>A boolean success indicator</li>
10998 * @param {Object} scope The scope in which to call the callback
10999 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11001 load : function(params, reader, callback, scope, arg){
11002 params = params || {};
11005 result = reader.readRecords(this.data);
11007 this.fireEvent("loadexception", this, arg, null, e);
11008 callback.call(scope, null, arg, false);
11011 callback.call(scope, result, arg, true);
11015 update : function(params, records){
11020 * Ext JS Library 1.1.1
11021 * Copyright(c) 2006-2007, Ext JS, LLC.
11023 * Originally Released Under LGPL - original licence link has changed is not relivant.
11026 * <script type="text/javascript">
11029 * @class Roo.data.HttpProxy
11030 * @extends Roo.data.DataProxy
11031 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11032 * configured to reference a certain URL.<br><br>
11034 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11035 * from which the running page was served.<br><br>
11037 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11039 * Be aware that to enable the browser to parse an XML document, the server must set
11040 * the Content-Type header in the HTTP response to "text/xml".
11042 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11043 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11044 * will be used to make the request.
11046 Roo.data.HttpProxy = function(conn){
11047 Roo.data.HttpProxy.superclass.constructor.call(this);
11048 // is conn a conn config or a real conn?
11050 this.useAjax = !conn || !conn.events;
11054 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11055 // thse are take from connection...
11058 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11061 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11062 * extra parameters to each request made by this object. (defaults to undefined)
11065 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11066 * to each request made by this object. (defaults to undefined)
11069 * @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)
11072 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11075 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11081 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11085 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11086 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11087 * a finer-grained basis than the DataProxy events.
11089 getConnection : function(){
11090 return this.useAjax ? Roo.Ajax : this.conn;
11094 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11095 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11096 * process that block using the passed callback.
11097 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11098 * for the request to the remote server.
11099 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11100 * object into a block of Roo.data.Records.
11101 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11102 * The function must be passed <ul>
11103 * <li>The Record block object</li>
11104 * <li>The "arg" argument from the load function</li>
11105 * <li>A boolean success indicator</li>
11107 * @param {Object} scope The scope in which to call the callback
11108 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11110 load : function(params, reader, callback, scope, arg){
11111 if(this.fireEvent("beforeload", this, params) !== false){
11113 params : params || {},
11115 callback : callback,
11120 callback : this.loadResponse,
11124 Roo.applyIf(o, this.conn);
11125 if(this.activeRequest){
11126 Roo.Ajax.abort(this.activeRequest);
11128 this.activeRequest = Roo.Ajax.request(o);
11130 this.conn.request(o);
11133 callback.call(scope||this, null, arg, false);
11138 loadResponse : function(o, success, response){
11139 delete this.activeRequest;
11141 this.fireEvent("loadexception", this, o, response);
11142 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11147 result = o.reader.read(response);
11149 this.fireEvent("loadexception", this, o, response, e);
11150 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11154 this.fireEvent("load", this, o, o.request.arg);
11155 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11159 update : function(dataSet){
11164 updateResponse : function(dataSet){
11169 * Ext JS Library 1.1.1
11170 * Copyright(c) 2006-2007, Ext JS, LLC.
11172 * Originally Released Under LGPL - original licence link has changed is not relivant.
11175 * <script type="text/javascript">
11179 * @class Roo.data.ScriptTagProxy
11180 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11181 * other than the originating domain of the running page.<br><br>
11183 * <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
11184 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11186 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11187 * source code that is used as the source inside a <script> tag.<br><br>
11189 * In order for the browser to process the returned data, the server must wrap the data object
11190 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11191 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11192 * depending on whether the callback name was passed:
11195 boolean scriptTag = false;
11196 String cb = request.getParameter("callback");
11199 response.setContentType("text/javascript");
11201 response.setContentType("application/x-json");
11203 Writer out = response.getWriter();
11205 out.write(cb + "(");
11207 out.print(dataBlock.toJsonString());
11214 * @param {Object} config A configuration object.
11216 Roo.data.ScriptTagProxy = function(config){
11217 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11218 Roo.apply(this, config);
11219 this.head = document.getElementsByTagName("head")[0];
11222 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11224 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11226 * @cfg {String} url The URL from which to request the data object.
11229 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11233 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11234 * the server the name of the callback function set up by the load call to process the returned data object.
11235 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11236 * javascript output which calls this named function passing the data object as its only parameter.
11238 callbackParam : "callback",
11240 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11241 * name to the request.
11246 * Load data from the configured URL, read the data object into
11247 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11248 * process that block using the passed callback.
11249 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11250 * for the request to the remote server.
11251 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11252 * object into a block of Roo.data.Records.
11253 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11254 * The function must be passed <ul>
11255 * <li>The Record block object</li>
11256 * <li>The "arg" argument from the load function</li>
11257 * <li>A boolean success indicator</li>
11259 * @param {Object} scope The scope in which to call the callback
11260 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11262 load : function(params, reader, callback, scope, arg){
11263 if(this.fireEvent("beforeload", this, params) !== false){
11265 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11267 var url = this.url;
11268 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11270 url += "&_dc=" + (new Date().getTime());
11272 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11275 cb : "stcCallback"+transId,
11276 scriptId : "stcScript"+transId,
11280 callback : callback,
11286 window[trans.cb] = function(o){
11287 conn.handleResponse(o, trans);
11290 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11292 if(this.autoAbort !== false){
11296 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11298 var script = document.createElement("script");
11299 script.setAttribute("src", url);
11300 script.setAttribute("type", "text/javascript");
11301 script.setAttribute("id", trans.scriptId);
11302 this.head.appendChild(script);
11304 this.trans = trans;
11306 callback.call(scope||this, null, arg, false);
11311 isLoading : function(){
11312 return this.trans ? true : false;
11316 * Abort the current server request.
11318 abort : function(){
11319 if(this.isLoading()){
11320 this.destroyTrans(this.trans);
11325 destroyTrans : function(trans, isLoaded){
11326 this.head.removeChild(document.getElementById(trans.scriptId));
11327 clearTimeout(trans.timeoutId);
11329 window[trans.cb] = undefined;
11331 delete window[trans.cb];
11334 // if hasn't been loaded, wait for load to remove it to prevent script error
11335 window[trans.cb] = function(){
11336 window[trans.cb] = undefined;
11338 delete window[trans.cb];
11345 handleResponse : function(o, trans){
11346 this.trans = false;
11347 this.destroyTrans(trans, true);
11350 result = trans.reader.readRecords(o);
11352 this.fireEvent("loadexception", this, o, trans.arg, e);
11353 trans.callback.call(trans.scope||window, null, trans.arg, false);
11356 this.fireEvent("load", this, o, trans.arg);
11357 trans.callback.call(trans.scope||window, result, trans.arg, true);
11361 handleFailure : function(trans){
11362 this.trans = false;
11363 this.destroyTrans(trans, false);
11364 this.fireEvent("loadexception", this, null, trans.arg);
11365 trans.callback.call(trans.scope||window, null, trans.arg, false);
11369 * Ext JS Library 1.1.1
11370 * Copyright(c) 2006-2007, Ext JS, LLC.
11372 * Originally Released Under LGPL - original licence link has changed is not relivant.
11375 * <script type="text/javascript">
11379 * @class Roo.data.JsonReader
11380 * @extends Roo.data.DataReader
11381 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11382 * based on mappings in a provided Roo.data.Record constructor.
11384 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11385 * in the reply previously.
11390 var RecordDef = Roo.data.Record.create([
11391 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11392 {name: 'occupation'} // This field will use "occupation" as the mapping.
11394 var myReader = new Roo.data.JsonReader({
11395 totalProperty: "results", // The property which contains the total dataset size (optional)
11396 root: "rows", // The property which contains an Array of row objects
11397 id: "id" // The property within each row object that provides an ID for the record (optional)
11401 * This would consume a JSON file like this:
11403 { 'results': 2, 'rows': [
11404 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11405 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11408 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11409 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11410 * paged from the remote server.
11411 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11412 * @cfg {String} root name of the property which contains the Array of row objects.
11413 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11414 * @cfg {Array} fields Array of field definition objects
11416 * Create a new JsonReader
11417 * @param {Object} meta Metadata configuration options
11418 * @param {Object} recordType Either an Array of field definition objects,
11419 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11421 Roo.data.JsonReader = function(meta, recordType){
11424 // set some defaults:
11425 Roo.applyIf(meta, {
11426 totalProperty: 'total',
11427 successProperty : 'success',
11432 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11434 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11437 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11438 * Used by Store query builder to append _requestMeta to params.
11441 metaFromRemote : false,
11443 * This method is only used by a DataProxy which has retrieved data from a remote server.
11444 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11445 * @return {Object} data A data block which is used by an Roo.data.Store object as
11446 * a cache of Roo.data.Records.
11448 read : function(response){
11449 var json = response.responseText;
11451 var o = /* eval:var:o */ eval("("+json+")");
11453 throw {message: "JsonReader.read: Json object not found"};
11459 this.metaFromRemote = true;
11460 this.meta = o.metaData;
11461 this.recordType = Roo.data.Record.create(o.metaData.fields);
11462 this.onMetaChange(this.meta, this.recordType, o);
11464 return this.readRecords(o);
11467 // private function a store will implement
11468 onMetaChange : function(meta, recordType, o){
11475 simpleAccess: function(obj, subsc) {
11482 getJsonAccessor: function(){
11484 return function(expr) {
11486 return(re.test(expr))
11487 ? new Function("obj", "return obj." + expr)
11492 return Roo.emptyFn;
11497 * Create a data block containing Roo.data.Records from an XML document.
11498 * @param {Object} o An object which contains an Array of row objects in the property specified
11499 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11500 * which contains the total size of the dataset.
11501 * @return {Object} data A data block which is used by an Roo.data.Store object as
11502 * a cache of Roo.data.Records.
11504 readRecords : function(o){
11506 * After any data loads, the raw JSON data is available for further custom processing.
11510 var s = this.meta, Record = this.recordType,
11511 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11513 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11515 if(s.totalProperty) {
11516 this.getTotal = this.getJsonAccessor(s.totalProperty);
11518 if(s.successProperty) {
11519 this.getSuccess = this.getJsonAccessor(s.successProperty);
11521 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11523 var g = this.getJsonAccessor(s.id);
11524 this.getId = function(rec) {
11526 return (r === undefined || r === "") ? null : r;
11529 this.getId = function(){return null;};
11532 for(var jj = 0; jj < fl; jj++){
11534 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11535 this.ef[jj] = this.getJsonAccessor(map);
11539 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11540 if(s.totalProperty){
11541 var vt = parseInt(this.getTotal(o), 10);
11546 if(s.successProperty){
11547 var vs = this.getSuccess(o);
11548 if(vs === false || vs === 'false'){
11553 for(var i = 0; i < c; i++){
11556 var id = this.getId(n);
11557 for(var j = 0; j < fl; j++){
11559 var v = this.ef[j](n);
11561 Roo.log('missing convert for ' + f.name);
11565 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11567 var record = new Record(values, id);
11569 records[i] = record;
11575 totalRecords : totalRecords
11580 * Ext JS Library 1.1.1
11581 * Copyright(c) 2006-2007, Ext JS, LLC.
11583 * Originally Released Under LGPL - original licence link has changed is not relivant.
11586 * <script type="text/javascript">
11590 * @class Roo.data.ArrayReader
11591 * @extends Roo.data.DataReader
11592 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11593 * Each element of that Array represents a row of data fields. The
11594 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11595 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11599 var RecordDef = Roo.data.Record.create([
11600 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11601 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11603 var myReader = new Roo.data.ArrayReader({
11604 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11608 * This would consume an Array like this:
11610 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11612 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11614 * Create a new JsonReader
11615 * @param {Object} meta Metadata configuration options.
11616 * @param {Object} recordType Either an Array of field definition objects
11617 * as specified to {@link Roo.data.Record#create},
11618 * or an {@link Roo.data.Record} object
11619 * created using {@link Roo.data.Record#create}.
11621 Roo.data.ArrayReader = function(meta, recordType){
11622 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11625 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11627 * Create a data block containing Roo.data.Records from an XML document.
11628 * @param {Object} o An Array of row objects which represents the dataset.
11629 * @return {Object} data A data block which is used by an Roo.data.Store object as
11630 * a cache of Roo.data.Records.
11632 readRecords : function(o){
11633 var sid = this.meta ? this.meta.id : null;
11634 var recordType = this.recordType, fields = recordType.prototype.fields;
11637 for(var i = 0; i < root.length; i++){
11640 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11641 for(var j = 0, jlen = fields.length; j < jlen; j++){
11642 var f = fields.items[j];
11643 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11644 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11646 values[f.name] = v;
11648 var record = new recordType(values, id);
11650 records[records.length] = record;
11654 totalRecords : records.length
11663 * @class Roo.bootstrap.ComboBox
11664 * @extends Roo.bootstrap.TriggerField
11665 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11666 * @cfg {Boolean} append (true|false) default false
11667 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11668 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11669 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11670 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11671 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11672 * @cfg {Boolean} animate default true
11673 * @cfg {Boolean} emptyResultText only for touch device
11674 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11676 * Create a new ComboBox.
11677 * @param {Object} config Configuration options
11679 Roo.bootstrap.ComboBox = function(config){
11680 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11684 * Fires when the dropdown list is expanded
11685 * @param {Roo.bootstrap.ComboBox} combo This combo box
11690 * Fires when the dropdown list is collapsed
11691 * @param {Roo.bootstrap.ComboBox} combo This combo box
11695 * @event beforeselect
11696 * Fires before a list item is selected. Return false to cancel the selection.
11697 * @param {Roo.bootstrap.ComboBox} combo This combo box
11698 * @param {Roo.data.Record} record The data record returned from the underlying store
11699 * @param {Number} index The index of the selected item in the dropdown list
11701 'beforeselect' : true,
11704 * Fires when a list item is selected
11705 * @param {Roo.bootstrap.ComboBox} combo This combo box
11706 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11707 * @param {Number} index The index of the selected item in the dropdown list
11711 * @event beforequery
11712 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11713 * The event object passed has these properties:
11714 * @param {Roo.bootstrap.ComboBox} combo This combo box
11715 * @param {String} query The query
11716 * @param {Boolean} forceAll true to force "all" query
11717 * @param {Boolean} cancel true to cancel the query
11718 * @param {Object} e The query event object
11720 'beforequery': true,
11723 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11724 * @param {Roo.bootstrap.ComboBox} combo This combo box
11729 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11730 * @param {Roo.bootstrap.ComboBox} combo This combo box
11731 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11736 * Fires when the remove value from the combobox array
11737 * @param {Roo.bootstrap.ComboBox} combo This combo box
11741 * @event afterremove
11742 * Fires when the remove value from the combobox array
11743 * @param {Roo.bootstrap.ComboBox} combo This combo box
11745 'afterremove' : true,
11747 * @event specialfilter
11748 * Fires when specialfilter
11749 * @param {Roo.bootstrap.ComboBox} combo This combo box
11751 'specialfilter' : true,
11754 * Fires when tick the element
11755 * @param {Roo.bootstrap.ComboBox} combo This combo box
11759 * @event touchviewdisplay
11760 * Fires when touch view require special display (default is using displayField)
11761 * @param {Roo.bootstrap.ComboBox} combo This combo box
11762 * @param {Object} cfg set html .
11764 'touchviewdisplay' : true
11769 this.tickItems = [];
11771 this.selectedIndex = -1;
11772 if(this.mode == 'local'){
11773 if(config.queryDelay === undefined){
11774 this.queryDelay = 10;
11776 if(config.minChars === undefined){
11782 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11785 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11786 * rendering into an Roo.Editor, defaults to false)
11789 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11790 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11793 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11796 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11797 * the dropdown list (defaults to undefined, with no header element)
11801 * @cfg {String/Roo.Template} tpl The template to use to render the output
11805 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11807 listWidth: undefined,
11809 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11810 * mode = 'remote' or 'text' if mode = 'local')
11812 displayField: undefined,
11815 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11816 * mode = 'remote' or 'value' if mode = 'local').
11817 * Note: use of a valueField requires the user make a selection
11818 * in order for a value to be mapped.
11820 valueField: undefined,
11824 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11825 * field's data value (defaults to the underlying DOM element's name)
11827 hiddenName: undefined,
11829 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11833 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11835 selectedClass: 'active',
11838 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11842 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11843 * anchor positions (defaults to 'tl-bl')
11845 listAlign: 'tl-bl?',
11847 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11851 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11852 * query specified by the allQuery config option (defaults to 'query')
11854 triggerAction: 'query',
11856 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11857 * (defaults to 4, does not apply if editable = false)
11861 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11862 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11866 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11867 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11871 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11872 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11876 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11877 * when editable = true (defaults to false)
11879 selectOnFocus:false,
11881 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11883 queryParam: 'query',
11885 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11886 * when mode = 'remote' (defaults to 'Loading...')
11888 loadingText: 'Loading...',
11890 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11894 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11898 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11899 * traditional select (defaults to true)
11903 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11907 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11911 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11912 * listWidth has a higher value)
11916 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11917 * allow the user to set arbitrary text into the field (defaults to false)
11919 forceSelection:false,
11921 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11922 * if typeAhead = true (defaults to 250)
11924 typeAheadDelay : 250,
11926 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11927 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11929 valueNotFoundText : undefined,
11931 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11933 blockFocus : false,
11936 * @cfg {Boolean} disableClear Disable showing of clear button.
11938 disableClear : false,
11940 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11942 alwaysQuery : false,
11945 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11950 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11952 invalidClass : "has-warning",
11955 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11957 validClass : "has-success",
11960 * @cfg {Boolean} specialFilter (true|false) special filter default false
11962 specialFilter : false,
11965 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11967 mobileTouchView : true,
11979 btnPosition : 'right',
11980 triggerList : true,
11981 showToggleBtn : true,
11983 emptyResultText: 'Empty',
11984 triggerText : 'Select',
11986 // element that contains real text value.. (when hidden is used..)
11988 getAutoCreate : function()
11996 if(Roo.isTouch && this.mobileTouchView){
11997 cfg = this.getAutoCreateTouchView();
12004 if(!this.tickable){
12005 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12010 * ComboBox with tickable selections
12013 var align = this.labelAlign || this.parentLabelAlign();
12016 cls : 'form-group roo-combobox-tickable' //input-group
12021 cls : 'tickable-buttons',
12026 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12027 html : this.triggerText
12033 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12040 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12047 buttons.cn.unshift({
12049 cls: 'roo-select2-search-field-input'
12055 Roo.each(buttons.cn, function(c){
12057 c.cls += ' btn-' + _this.size;
12060 if (_this.disabled) {
12071 cls: 'form-hidden-field'
12075 cls: 'roo-select2-choices',
12079 cls: 'roo-select2-search-field',
12091 cls: 'roo-select2-container input-group roo-select2-container-multi',
12096 // cls: 'typeahead typeahead-long dropdown-menu',
12097 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12102 if(this.hasFeedback && !this.allowBlank){
12106 cls: 'glyphicon form-control-feedback'
12109 combobox.cn.push(feedback);
12112 if (align ==='left' && this.fieldLabel.length) {
12114 // Roo.log("left and has label");
12120 cls : 'control-label col-sm-' + this.labelWidth,
12121 html : this.fieldLabel
12125 cls : "col-sm-" + (12 - this.labelWidth),
12132 } else if ( this.fieldLabel.length) {
12133 // Roo.log(" label");
12138 //cls : 'input-group-addon',
12139 html : this.fieldLabel
12149 // Roo.log(" no label && no align");
12156 ['xs','sm','md','lg'].map(function(size){
12157 if (settings[size]) {
12158 cfg.cls += ' col-' + size + '-' + settings[size];
12166 _initEventsCalled : false,
12169 initEvents: function()
12172 if (this._initEventsCalled) { // as we call render... prevent looping...
12175 this._initEventsCalled = true;
12178 throw "can not find store for combo";
12181 this.store = Roo.factory(this.store, Roo.data);
12183 // if we are building from html. then this element is so complex, that we can not really
12184 // use the rendered HTML.
12185 // so we have to trash and replace the previous code.
12186 if (Roo.XComponent.build_from_html) {
12188 // remove this element....
12189 var e = this.el.dom, k=0;
12190 while (e ) { e = e.previousSibling; ++k;}
12195 this.rendered = false;
12197 this.render(this.parent().getChildContainer(true), k);
12208 if(Roo.isTouch && this.mobileTouchView){
12209 this.initTouchView();
12214 this.initTickableEvents();
12218 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12220 if(this.hiddenName){
12222 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12224 this.hiddenField.dom.value =
12225 this.hiddenValue !== undefined ? this.hiddenValue :
12226 this.value !== undefined ? this.value : '';
12228 // prevent input submission
12229 this.el.dom.removeAttribute('name');
12230 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12235 // this.el.dom.setAttribute('autocomplete', 'off');
12238 var cls = 'x-combo-list';
12240 //this.list = new Roo.Layer({
12241 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12247 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12248 _this.list.setWidth(lw);
12251 this.list.on('mouseover', this.onViewOver, this);
12252 this.list.on('mousemove', this.onViewMove, this);
12254 this.list.on('scroll', this.onViewScroll, this);
12257 this.list.swallowEvent('mousewheel');
12258 this.assetHeight = 0;
12261 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12262 this.assetHeight += this.header.getHeight();
12265 this.innerList = this.list.createChild({cls:cls+'-inner'});
12266 this.innerList.on('mouseover', this.onViewOver, this);
12267 this.innerList.on('mousemove', this.onViewMove, this);
12268 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12270 if(this.allowBlank && !this.pageSize && !this.disableClear){
12271 this.footer = this.list.createChild({cls:cls+'-ft'});
12272 this.pageTb = new Roo.Toolbar(this.footer);
12276 this.footer = this.list.createChild({cls:cls+'-ft'});
12277 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12278 {pageSize: this.pageSize});
12282 if (this.pageTb && this.allowBlank && !this.disableClear) {
12284 this.pageTb.add(new Roo.Toolbar.Fill(), {
12285 cls: 'x-btn-icon x-btn-clear',
12287 handler: function()
12290 _this.clearValue();
12291 _this.onSelect(false, -1);
12296 this.assetHeight += this.footer.getHeight();
12301 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12304 this.view = new Roo.View(this.list, this.tpl, {
12305 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12307 //this.view.wrapEl.setDisplayed(false);
12308 this.view.on('click', this.onViewClick, this);
12312 this.store.on('beforeload', this.onBeforeLoad, this);
12313 this.store.on('load', this.onLoad, this);
12314 this.store.on('loadexception', this.onLoadException, this);
12316 if(this.resizable){
12317 this.resizer = new Roo.Resizable(this.list, {
12318 pinned:true, handles:'se'
12320 this.resizer.on('resize', function(r, w, h){
12321 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12322 this.listWidth = w;
12323 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12324 this.restrictHeight();
12326 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12329 if(!this.editable){
12330 this.editable = true;
12331 this.setEditable(false);
12336 if (typeof(this.events.add.listeners) != 'undefined') {
12338 this.addicon = this.wrap.createChild(
12339 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12341 this.addicon.on('click', function(e) {
12342 this.fireEvent('add', this);
12345 if (typeof(this.events.edit.listeners) != 'undefined') {
12347 this.editicon = this.wrap.createChild(
12348 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12349 if (this.addicon) {
12350 this.editicon.setStyle('margin-left', '40px');
12352 this.editicon.on('click', function(e) {
12354 // we fire even if inothing is selected..
12355 this.fireEvent('edit', this, this.lastData );
12361 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12362 "up" : function(e){
12363 this.inKeyMode = true;
12367 "down" : function(e){
12368 if(!this.isExpanded()){
12369 this.onTriggerClick();
12371 this.inKeyMode = true;
12376 "enter" : function(e){
12377 // this.onViewClick();
12381 if(this.fireEvent("specialkey", this, e)){
12382 this.onViewClick(false);
12388 "esc" : function(e){
12392 "tab" : function(e){
12395 if(this.fireEvent("specialkey", this, e)){
12396 this.onViewClick(false);
12404 doRelay : function(foo, bar, hname){
12405 if(hname == 'down' || this.scope.isExpanded()){
12406 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12415 this.queryDelay = Math.max(this.queryDelay || 10,
12416 this.mode == 'local' ? 10 : 250);
12419 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12421 if(this.typeAhead){
12422 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12424 if(this.editable !== false){
12425 this.inputEl().on("keyup", this.onKeyUp, this);
12427 if(this.forceSelection){
12428 this.inputEl().on('blur', this.doForce, this);
12432 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12433 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12437 initTickableEvents: function()
12441 if(this.hiddenName){
12443 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12445 this.hiddenField.dom.value =
12446 this.hiddenValue !== undefined ? this.hiddenValue :
12447 this.value !== undefined ? this.value : '';
12449 // prevent input submission
12450 this.el.dom.removeAttribute('name');
12451 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12456 // this.list = this.el.select('ul.dropdown-menu',true).first();
12458 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12459 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12460 if(this.triggerList){
12461 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12464 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12465 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12467 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12468 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12470 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12471 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12473 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12474 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12475 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12478 this.cancelBtn.hide();
12483 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12484 _this.list.setWidth(lw);
12487 this.list.on('mouseover', this.onViewOver, this);
12488 this.list.on('mousemove', this.onViewMove, this);
12490 this.list.on('scroll', this.onViewScroll, this);
12493 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>';
12496 this.view = new Roo.View(this.list, this.tpl, {
12497 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12500 //this.view.wrapEl.setDisplayed(false);
12501 this.view.on('click', this.onViewClick, this);
12505 this.store.on('beforeload', this.onBeforeLoad, this);
12506 this.store.on('load', this.onLoad, this);
12507 this.store.on('loadexception', this.onLoadException, this);
12510 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12511 "up" : function(e){
12512 this.inKeyMode = true;
12516 "down" : function(e){
12517 this.inKeyMode = true;
12521 "enter" : function(e){
12522 if(this.fireEvent("specialkey", this, e)){
12523 this.onViewClick(false);
12529 "esc" : function(e){
12530 this.onTickableFooterButtonClick(e, false, false);
12533 "tab" : function(e){
12534 this.fireEvent("specialkey", this, e);
12536 this.onTickableFooterButtonClick(e, false, false);
12543 doRelay : function(e, fn, key){
12544 if(this.scope.isExpanded()){
12545 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12554 this.queryDelay = Math.max(this.queryDelay || 10,
12555 this.mode == 'local' ? 10 : 250);
12558 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12560 if(this.typeAhead){
12561 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12564 if(this.editable !== false){
12565 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12570 onDestroy : function(){
12572 this.view.setStore(null);
12573 this.view.el.removeAllListeners();
12574 this.view.el.remove();
12575 this.view.purgeListeners();
12578 this.list.dom.innerHTML = '';
12582 this.store.un('beforeload', this.onBeforeLoad, this);
12583 this.store.un('load', this.onLoad, this);
12584 this.store.un('loadexception', this.onLoadException, this);
12586 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12590 fireKey : function(e){
12591 if(e.isNavKeyPress() && !this.list.isVisible()){
12592 this.fireEvent("specialkey", this, e);
12597 onResize: function(w, h){
12598 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12600 // if(typeof w != 'number'){
12601 // // we do not handle it!?!?
12604 // var tw = this.trigger.getWidth();
12605 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12606 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12608 // this.inputEl().setWidth( this.adjustWidth('input', x));
12610 // //this.trigger.setStyle('left', x+'px');
12612 // if(this.list && this.listWidth === undefined){
12613 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12614 // this.list.setWidth(lw);
12615 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12623 * Allow or prevent the user from directly editing the field text. If false is passed,
12624 * the user will only be able to select from the items defined in the dropdown list. This method
12625 * is the runtime equivalent of setting the 'editable' config option at config time.
12626 * @param {Boolean} value True to allow the user to directly edit the field text
12628 setEditable : function(value){
12629 if(value == this.editable){
12632 this.editable = value;
12634 this.inputEl().dom.setAttribute('readOnly', true);
12635 this.inputEl().on('mousedown', this.onTriggerClick, this);
12636 this.inputEl().addClass('x-combo-noedit');
12638 this.inputEl().dom.setAttribute('readOnly', false);
12639 this.inputEl().un('mousedown', this.onTriggerClick, this);
12640 this.inputEl().removeClass('x-combo-noedit');
12646 onBeforeLoad : function(combo,opts){
12647 if(!this.hasFocus){
12651 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12653 this.restrictHeight();
12654 this.selectedIndex = -1;
12658 onLoad : function(){
12660 this.hasQuery = false;
12662 if(!this.hasFocus){
12666 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12667 this.loading.hide();
12670 if(this.store.getCount() > 0){
12672 this.restrictHeight();
12673 if(this.lastQuery == this.allQuery){
12674 if(this.editable && !this.tickable){
12675 this.inputEl().dom.select();
12679 !this.selectByValue(this.value, true) &&
12682 !this.store.lastOptions ||
12683 typeof(this.store.lastOptions.add) == 'undefined' ||
12684 this.store.lastOptions.add != true
12687 this.select(0, true);
12690 if(this.autoFocus){
12693 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12694 this.taTask.delay(this.typeAheadDelay);
12698 this.onEmptyResults();
12704 onLoadException : function()
12706 this.hasQuery = false;
12708 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12709 this.loading.hide();
12712 if(this.tickable && this.editable){
12717 // only causes errors at present
12718 //Roo.log(this.store.reader.jsonData);
12719 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12721 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12727 onTypeAhead : function(){
12728 if(this.store.getCount() > 0){
12729 var r = this.store.getAt(0);
12730 var newValue = r.data[this.displayField];
12731 var len = newValue.length;
12732 var selStart = this.getRawValue().length;
12734 if(selStart != len){
12735 this.setRawValue(newValue);
12736 this.selectText(selStart, newValue.length);
12742 onSelect : function(record, index){
12744 if(this.fireEvent('beforeselect', this, record, index) !== false){
12746 this.setFromData(index > -1 ? record.data : false);
12749 this.fireEvent('select', this, record, index);
12754 * Returns the currently selected field value or empty string if no value is set.
12755 * @return {String} value The selected value
12757 getValue : function(){
12760 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12763 if(this.valueField){
12764 return typeof this.value != 'undefined' ? this.value : '';
12766 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12771 * Clears any text/value currently set in the field
12773 clearValue : function(){
12774 if(this.hiddenField){
12775 this.hiddenField.dom.value = '';
12778 this.setRawValue('');
12779 this.lastSelectionText = '';
12780 this.lastData = false;
12782 var close = this.closeTriggerEl();
12791 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12792 * will be displayed in the field. If the value does not match the data value of an existing item,
12793 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12794 * Otherwise the field will be blank (although the value will still be set).
12795 * @param {String} value The value to match
12797 setValue : function(v){
12804 if(this.valueField){
12805 var r = this.findRecord(this.valueField, v);
12807 text = r.data[this.displayField];
12808 }else if(this.valueNotFoundText !== undefined){
12809 text = this.valueNotFoundText;
12812 this.lastSelectionText = text;
12813 if(this.hiddenField){
12814 this.hiddenField.dom.value = v;
12816 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12819 var close = this.closeTriggerEl();
12822 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12826 * @property {Object} the last set data for the element
12831 * Sets the value of the field based on a object which is related to the record format for the store.
12832 * @param {Object} value the value to set as. or false on reset?
12834 setFromData : function(o){
12841 var dv = ''; // display value
12842 var vv = ''; // value value..
12844 if (this.displayField) {
12845 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12847 // this is an error condition!!!
12848 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12851 if(this.valueField){
12852 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12855 var close = this.closeTriggerEl();
12858 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12861 if(this.hiddenField){
12862 this.hiddenField.dom.value = vv;
12864 this.lastSelectionText = dv;
12865 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12869 // no hidden field.. - we store the value in 'value', but still display
12870 // display field!!!!
12871 this.lastSelectionText = dv;
12872 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12879 reset : function(){
12880 // overridden so that last data is reset..
12887 this.setValue(this.originalValue);
12888 this.clearInvalid();
12889 this.lastData = false;
12891 this.view.clearSelections();
12895 findRecord : function(prop, value){
12897 if(this.store.getCount() > 0){
12898 this.store.each(function(r){
12899 if(r.data[prop] == value){
12909 getName: function()
12911 // returns hidden if it's set..
12912 if (!this.rendered) {return ''};
12913 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12917 onViewMove : function(e, t){
12918 this.inKeyMode = false;
12922 onViewOver : function(e, t){
12923 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12926 var item = this.view.findItemFromChild(t);
12929 var index = this.view.indexOf(item);
12930 this.select(index, false);
12935 onViewClick : function(view, doFocus, el, e)
12937 var index = this.view.getSelectedIndexes()[0];
12939 var r = this.store.getAt(index);
12943 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12950 Roo.each(this.tickItems, function(v,k){
12952 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12954 _this.tickItems.splice(k, 1);
12956 if(typeof(e) == 'undefined' && view == false){
12957 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12969 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12970 this.tickItems.push(r.data);
12973 if(typeof(e) == 'undefined' && view == false){
12974 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12981 this.onSelect(r, index);
12983 if(doFocus !== false && !this.blockFocus){
12984 this.inputEl().focus();
12989 restrictHeight : function(){
12990 //this.innerList.dom.style.height = '';
12991 //var inner = this.innerList.dom;
12992 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12993 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12994 //this.list.beginUpdate();
12995 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12996 this.list.alignTo(this.inputEl(), this.listAlign);
12997 this.list.alignTo(this.inputEl(), this.listAlign);
12998 //this.list.endUpdate();
13002 onEmptyResults : function(){
13004 if(this.tickable && this.editable){
13005 this.restrictHeight();
13013 * Returns true if the dropdown list is expanded, else false.
13015 isExpanded : function(){
13016 return this.list.isVisible();
13020 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13021 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13022 * @param {String} value The data value of the item to select
13023 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13024 * selected item if it is not currently in view (defaults to true)
13025 * @return {Boolean} True if the value matched an item in the list, else false
13027 selectByValue : function(v, scrollIntoView){
13028 if(v !== undefined && v !== null){
13029 var r = this.findRecord(this.valueField || this.displayField, v);
13031 this.select(this.store.indexOf(r), scrollIntoView);
13039 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13040 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13041 * @param {Number} index The zero-based index of the list item to select
13042 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13043 * selected item if it is not currently in view (defaults to true)
13045 select : function(index, scrollIntoView){
13046 this.selectedIndex = index;
13047 this.view.select(index);
13048 if(scrollIntoView !== false){
13049 var el = this.view.getNode(index);
13051 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13054 this.list.scrollChildIntoView(el, false);
13060 selectNext : function(){
13061 var ct = this.store.getCount();
13063 if(this.selectedIndex == -1){
13065 }else if(this.selectedIndex < ct-1){
13066 this.select(this.selectedIndex+1);
13072 selectPrev : function(){
13073 var ct = this.store.getCount();
13075 if(this.selectedIndex == -1){
13077 }else if(this.selectedIndex != 0){
13078 this.select(this.selectedIndex-1);
13084 onKeyUp : function(e){
13085 if(this.editable !== false && !e.isSpecialKey()){
13086 this.lastKey = e.getKey();
13087 this.dqTask.delay(this.queryDelay);
13092 validateBlur : function(){
13093 return !this.list || !this.list.isVisible();
13097 initQuery : function(){
13099 var v = this.getRawValue();
13101 if(this.tickable && this.editable){
13102 v = this.tickableInputEl().getValue();
13109 doForce : function(){
13110 if(this.inputEl().dom.value.length > 0){
13111 this.inputEl().dom.value =
13112 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13118 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13119 * query allowing the query action to be canceled if needed.
13120 * @param {String} query The SQL query to execute
13121 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13122 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13123 * saved in the current store (defaults to false)
13125 doQuery : function(q, forceAll){
13127 if(q === undefined || q === null){
13132 forceAll: forceAll,
13136 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13141 forceAll = qe.forceAll;
13142 if(forceAll === true || (q.length >= this.minChars)){
13144 this.hasQuery = true;
13146 if(this.lastQuery != q || this.alwaysQuery){
13147 this.lastQuery = q;
13148 if(this.mode == 'local'){
13149 this.selectedIndex = -1;
13151 this.store.clearFilter();
13154 if(this.specialFilter){
13155 this.fireEvent('specialfilter', this);
13160 this.store.filter(this.displayField, q);
13163 this.store.fireEvent("datachanged", this.store);
13170 this.store.baseParams[this.queryParam] = q;
13172 var options = {params : this.getParams(q)};
13175 options.add = true;
13176 options.params.start = this.page * this.pageSize;
13179 this.store.load(options);
13182 * this code will make the page width larger, at the beginning, the list not align correctly,
13183 * we should expand the list on onLoad
13184 * so command out it
13189 this.selectedIndex = -1;
13194 this.loadNext = false;
13198 getParams : function(q){
13200 //p[this.queryParam] = q;
13204 p.limit = this.pageSize;
13210 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13212 collapse : function(){
13213 if(!this.isExpanded()){
13220 this.hasFocus = false;
13222 this.cancelBtn.hide();
13223 this.trigger.show();
13226 this.tickableInputEl().dom.value = '';
13227 this.tickableInputEl().blur();
13232 Roo.get(document).un('mousedown', this.collapseIf, this);
13233 Roo.get(document).un('mousewheel', this.collapseIf, this);
13234 if (!this.editable) {
13235 Roo.get(document).un('keydown', this.listKeyPress, this);
13237 this.fireEvent('collapse', this);
13241 collapseIf : function(e){
13242 var in_combo = e.within(this.el);
13243 var in_list = e.within(this.list);
13244 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13246 if (in_combo || in_list || is_list) {
13247 //e.stopPropagation();
13252 this.onTickableFooterButtonClick(e, false, false);
13260 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13262 expand : function(){
13264 if(this.isExpanded() || !this.hasFocus){
13268 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13269 this.list.setWidth(lw);
13276 this.restrictHeight();
13280 this.tickItems = Roo.apply([], this.item);
13283 this.cancelBtn.show();
13284 this.trigger.hide();
13287 this.tickableInputEl().focus();
13292 Roo.get(document).on('mousedown', this.collapseIf, this);
13293 Roo.get(document).on('mousewheel', this.collapseIf, this);
13294 if (!this.editable) {
13295 Roo.get(document).on('keydown', this.listKeyPress, this);
13298 this.fireEvent('expand', this);
13302 // Implements the default empty TriggerField.onTriggerClick function
13303 onTriggerClick : function(e)
13305 Roo.log('trigger click');
13307 if(this.disabled || !this.triggerList){
13312 this.loadNext = false;
13314 if(this.isExpanded()){
13316 if (!this.blockFocus) {
13317 this.inputEl().focus();
13321 this.hasFocus = true;
13322 if(this.triggerAction == 'all') {
13323 this.doQuery(this.allQuery, true);
13325 this.doQuery(this.getRawValue());
13327 if (!this.blockFocus) {
13328 this.inputEl().focus();
13333 onTickableTriggerClick : function(e)
13340 this.loadNext = false;
13341 this.hasFocus = true;
13343 if(this.triggerAction == 'all') {
13344 this.doQuery(this.allQuery, true);
13346 this.doQuery(this.getRawValue());
13350 onSearchFieldClick : function(e)
13352 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13353 this.onTickableFooterButtonClick(e, false, false);
13357 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13362 this.loadNext = false;
13363 this.hasFocus = true;
13365 if(this.triggerAction == 'all') {
13366 this.doQuery(this.allQuery, true);
13368 this.doQuery(this.getRawValue());
13372 listKeyPress : function(e)
13374 //Roo.log('listkeypress');
13375 // scroll to first matching element based on key pres..
13376 if (e.isSpecialKey()) {
13379 var k = String.fromCharCode(e.getKey()).toUpperCase();
13382 var csel = this.view.getSelectedNodes();
13383 var cselitem = false;
13385 var ix = this.view.indexOf(csel[0]);
13386 cselitem = this.store.getAt(ix);
13387 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13393 this.store.each(function(v) {
13395 // start at existing selection.
13396 if (cselitem.id == v.id) {
13402 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13403 match = this.store.indexOf(v);
13409 if (match === false) {
13410 return true; // no more action?
13413 this.view.select(match);
13414 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13415 sn.scrollIntoView(sn.dom.parentNode, false);
13418 onViewScroll : function(e, t){
13420 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){
13424 this.hasQuery = true;
13426 this.loading = this.list.select('.loading', true).first();
13428 if(this.loading === null){
13429 this.list.createChild({
13431 cls: 'loading roo-select2-more-results roo-select2-active',
13432 html: 'Loading more results...'
13435 this.loading = this.list.select('.loading', true).first();
13437 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13439 this.loading.hide();
13442 this.loading.show();
13447 this.loadNext = true;
13449 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13454 addItem : function(o)
13456 var dv = ''; // display value
13458 if (this.displayField) {
13459 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13461 // this is an error condition!!!
13462 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13469 var choice = this.choices.createChild({
13471 cls: 'roo-select2-search-choice',
13480 cls: 'roo-select2-search-choice-close',
13485 }, this.searchField);
13487 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13489 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13497 this.inputEl().dom.value = '';
13502 onRemoveItem : function(e, _self, o)
13504 e.preventDefault();
13506 this.lastItem = Roo.apply([], this.item);
13508 var index = this.item.indexOf(o.data) * 1;
13511 Roo.log('not this item?!');
13515 this.item.splice(index, 1);
13520 this.fireEvent('remove', this, e);
13526 syncValue : function()
13528 if(!this.item.length){
13535 Roo.each(this.item, function(i){
13536 if(_this.valueField){
13537 value.push(i[_this.valueField]);
13544 this.value = value.join(',');
13546 if(this.hiddenField){
13547 this.hiddenField.dom.value = this.value;
13550 this.store.fireEvent("datachanged", this.store);
13553 clearItem : function()
13555 if(!this.multiple){
13561 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13569 if(this.tickable && !Roo.isTouch){
13570 this.view.refresh();
13574 inputEl: function ()
13576 if(Roo.isTouch && this.mobileTouchView){
13577 return this.el.select('input.form-control',true).first();
13581 return this.searchField;
13584 return this.el.select('input.form-control',true).first();
13588 onTickableFooterButtonClick : function(e, btn, el)
13590 e.preventDefault();
13592 this.lastItem = Roo.apply([], this.item);
13594 if(btn && btn.name == 'cancel'){
13595 this.tickItems = Roo.apply([], this.item);
13604 Roo.each(this.tickItems, function(o){
13612 validate : function()
13614 var v = this.getRawValue();
13617 v = this.getValue();
13620 if(this.disabled || this.allowBlank || v.length){
13625 this.markInvalid();
13629 tickableInputEl : function()
13631 if(!this.tickable || !this.editable){
13632 return this.inputEl();
13635 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13639 getAutoCreateTouchView : function()
13644 cls: 'form-group' //input-group
13650 type : this.inputType,
13651 cls : 'form-control x-combo-noedit',
13652 autocomplete: 'new-password',
13653 placeholder : this.placeholder || '',
13658 input.name = this.name;
13662 input.cls += ' input-' + this.size;
13665 if (this.disabled) {
13666 input.disabled = true;
13677 inputblock.cls += ' input-group';
13679 inputblock.cn.unshift({
13681 cls : 'input-group-addon',
13686 if(this.removable && !this.multiple){
13687 inputblock.cls += ' roo-removable';
13689 inputblock.cn.push({
13692 cls : 'roo-combo-removable-btn close'
13696 if(this.hasFeedback && !this.allowBlank){
13698 inputblock.cls += ' has-feedback';
13700 inputblock.cn.push({
13702 cls: 'glyphicon form-control-feedback'
13709 inputblock.cls += (this.before) ? '' : ' input-group';
13711 inputblock.cn.push({
13713 cls : 'input-group-addon',
13724 cls: 'form-hidden-field'
13738 cls: 'form-hidden-field'
13742 cls: 'roo-select2-choices',
13746 cls: 'roo-select2-search-field',
13759 cls: 'roo-select2-container input-group',
13766 combobox.cls += ' roo-select2-container-multi';
13769 var align = this.labelAlign || this.parentLabelAlign();
13773 if(this.fieldLabel.length){
13775 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13776 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13781 cls : 'control-label ' + lw,
13782 html : this.fieldLabel
13794 var settings = this;
13796 ['xs','sm','md','lg'].map(function(size){
13797 if (settings[size]) {
13798 cfg.cls += ' col-' + size + '-' + settings[size];
13805 initTouchView : function()
13807 this.renderTouchView();
13809 this.touchViewEl.on('scroll', function(){
13810 this.el.dom.scrollTop = 0;
13813 this.originalValue = this.getValue();
13815 this.inputEl().on("click", this.showTouchView, this);
13817 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13818 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13820 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13822 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13823 this.store.on('load', this.onTouchViewLoad, this);
13824 this.store.on('loadexception', this.onTouchViewLoadException, this);
13826 if(this.hiddenName){
13828 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13830 this.hiddenField.dom.value =
13831 this.hiddenValue !== undefined ? this.hiddenValue :
13832 this.value !== undefined ? this.value : '';
13834 this.el.dom.removeAttribute('name');
13835 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13839 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13840 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13843 if(this.removable && !this.multiple){
13844 var close = this.closeTriggerEl();
13846 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13847 close.on('click', this.removeBtnClick, this, close);
13851 * fix the bug in Safari iOS8
13853 this.inputEl().on("focus", function(e){
13854 document.activeElement.blur();
13862 renderTouchView : function()
13864 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13865 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13867 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13868 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13870 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13871 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13872 this.touchViewBodyEl.setStyle('overflow', 'auto');
13874 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13875 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13877 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13878 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13882 showTouchView : function()
13888 this.touchViewHeaderEl.hide();
13890 if(this.fieldLabel.length){
13891 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13892 this.touchViewHeaderEl.show();
13895 this.touchViewEl.show();
13897 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13898 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13900 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13902 if(this.fieldLabel.length){
13903 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13906 this.touchViewBodyEl.setHeight(bodyHeight);
13910 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13912 this.touchViewEl.addClass('in');
13915 this.doTouchViewQuery();
13919 hideTouchView : function()
13921 this.touchViewEl.removeClass('in');
13925 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13927 this.touchViewEl.setStyle('display', 'none');
13932 setTouchViewValue : function()
13939 Roo.each(this.tickItems, function(o){
13944 this.hideTouchView();
13947 doTouchViewQuery : function()
13956 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13960 if(!this.alwaysQuery || this.mode == 'local'){
13961 this.onTouchViewLoad();
13968 onTouchViewBeforeLoad : function(combo,opts)
13974 onTouchViewLoad : function()
13976 if(this.store.getCount() < 1){
13977 this.onTouchViewEmptyResults();
13981 this.clearTouchView();
13983 var rawValue = this.getRawValue();
13985 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13987 this.tickItems = [];
13989 this.store.data.each(function(d, rowIndex){
13990 var row = this.touchViewListGroup.createChild(template);
13992 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
13993 row.addClass(d.data.cls);
13996 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13999 html : d.data[this.displayField]
14002 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14003 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14007 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14008 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14011 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14012 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14013 this.tickItems.push(d.data);
14016 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14020 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14022 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14024 if(this.fieldLabel.length){
14025 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14028 var listHeight = this.touchViewListGroup.getHeight();
14032 if(firstChecked && listHeight > bodyHeight){
14033 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14038 onTouchViewLoadException : function()
14040 this.hideTouchView();
14043 onTouchViewEmptyResults : function()
14045 this.clearTouchView();
14047 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14049 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14053 clearTouchView : function()
14055 this.touchViewListGroup.dom.innerHTML = '';
14058 onTouchViewClick : function(e, el, o)
14060 e.preventDefault();
14063 var rowIndex = o.rowIndex;
14065 var r = this.store.getAt(rowIndex);
14067 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14069 if(!this.multiple){
14070 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14071 c.dom.removeAttribute('checked');
14074 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14076 this.setFromData(r.data);
14078 var close = this.closeTriggerEl();
14084 this.hideTouchView();
14086 this.fireEvent('select', this, r, rowIndex);
14091 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14092 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14093 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14097 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14098 this.addItem(r.data);
14099 this.tickItems.push(r.data);
14105 * @cfg {Boolean} grow
14109 * @cfg {Number} growMin
14113 * @cfg {Number} growMax
14122 Roo.apply(Roo.bootstrap.ComboBox, {
14126 cls: 'modal-header',
14148 cls: 'list-group-item',
14152 cls: 'roo-combobox-list-group-item-value'
14156 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14170 listItemCheckbox : {
14172 cls: 'list-group-item',
14176 cls: 'roo-combobox-list-group-item-value'
14180 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14196 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14201 cls: 'modal-footer',
14209 cls: 'col-xs-6 text-left',
14212 cls: 'btn btn-danger roo-touch-view-cancel',
14218 cls: 'col-xs-6 text-right',
14221 cls: 'btn btn-success roo-touch-view-ok',
14232 Roo.apply(Roo.bootstrap.ComboBox, {
14234 touchViewTemplate : {
14236 cls: 'modal fade roo-combobox-touch-view',
14240 cls: 'modal-dialog',
14241 style : 'position:fixed', // we have to fix position....
14245 cls: 'modal-content',
14247 Roo.bootstrap.ComboBox.header,
14248 Roo.bootstrap.ComboBox.body,
14249 Roo.bootstrap.ComboBox.footer
14258 * Ext JS Library 1.1.1
14259 * Copyright(c) 2006-2007, Ext JS, LLC.
14261 * Originally Released Under LGPL - original licence link has changed is not relivant.
14264 * <script type="text/javascript">
14269 * @extends Roo.util.Observable
14270 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14271 * This class also supports single and multi selection modes. <br>
14272 * Create a data model bound view:
14274 var store = new Roo.data.Store(...);
14276 var view = new Roo.View({
14278 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14280 singleSelect: true,
14281 selectedClass: "ydataview-selected",
14285 // listen for node click?
14286 view.on("click", function(vw, index, node, e){
14287 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14291 dataModel.load("foobar.xml");
14293 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14295 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14296 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14298 * Note: old style constructor is still suported (container, template, config)
14301 * Create a new View
14302 * @param {Object} config The config object
14305 Roo.View = function(config, depreciated_tpl, depreciated_config){
14307 this.parent = false;
14309 if (typeof(depreciated_tpl) == 'undefined') {
14310 // new way.. - universal constructor.
14311 Roo.apply(this, config);
14312 this.el = Roo.get(this.el);
14315 this.el = Roo.get(config);
14316 this.tpl = depreciated_tpl;
14317 Roo.apply(this, depreciated_config);
14319 this.wrapEl = this.el.wrap().wrap();
14320 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14323 if(typeof(this.tpl) == "string"){
14324 this.tpl = new Roo.Template(this.tpl);
14326 // support xtype ctors..
14327 this.tpl = new Roo.factory(this.tpl, Roo);
14331 this.tpl.compile();
14336 * @event beforeclick
14337 * Fires before a click is processed. Returns false to cancel the default action.
14338 * @param {Roo.View} this
14339 * @param {Number} index The index of the target node
14340 * @param {HTMLElement} node The target node
14341 * @param {Roo.EventObject} e The raw event object
14343 "beforeclick" : true,
14346 * Fires when a template node is clicked.
14347 * @param {Roo.View} this
14348 * @param {Number} index The index of the target node
14349 * @param {HTMLElement} node The target node
14350 * @param {Roo.EventObject} e The raw event object
14355 * Fires when a template node is double clicked.
14356 * @param {Roo.View} this
14357 * @param {Number} index The index of the target node
14358 * @param {HTMLElement} node The target node
14359 * @param {Roo.EventObject} e The raw event object
14363 * @event contextmenu
14364 * Fires when a template node is right clicked.
14365 * @param {Roo.View} this
14366 * @param {Number} index The index of the target node
14367 * @param {HTMLElement} node The target node
14368 * @param {Roo.EventObject} e The raw event object
14370 "contextmenu" : true,
14372 * @event selectionchange
14373 * Fires when the selected nodes change.
14374 * @param {Roo.View} this
14375 * @param {Array} selections Array of the selected nodes
14377 "selectionchange" : true,
14380 * @event beforeselect
14381 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14382 * @param {Roo.View} this
14383 * @param {HTMLElement} node The node to be selected
14384 * @param {Array} selections Array of currently selected nodes
14386 "beforeselect" : true,
14388 * @event preparedata
14389 * Fires on every row to render, to allow you to change the data.
14390 * @param {Roo.View} this
14391 * @param {Object} data to be rendered (change this)
14393 "preparedata" : true
14401 "click": this.onClick,
14402 "dblclick": this.onDblClick,
14403 "contextmenu": this.onContextMenu,
14407 this.selections = [];
14409 this.cmp = new Roo.CompositeElementLite([]);
14411 this.store = Roo.factory(this.store, Roo.data);
14412 this.setStore(this.store, true);
14415 if ( this.footer && this.footer.xtype) {
14417 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14419 this.footer.dataSource = this.store;
14420 this.footer.container = fctr;
14421 this.footer = Roo.factory(this.footer, Roo);
14422 fctr.insertFirst(this.el);
14424 // this is a bit insane - as the paging toolbar seems to detach the el..
14425 // dom.parentNode.parentNode.parentNode
14426 // they get detached?
14430 Roo.View.superclass.constructor.call(this);
14435 Roo.extend(Roo.View, Roo.util.Observable, {
14438 * @cfg {Roo.data.Store} store Data store to load data from.
14443 * @cfg {String|Roo.Element} el The container element.
14448 * @cfg {String|Roo.Template} tpl The template used by this View
14452 * @cfg {String} dataName the named area of the template to use as the data area
14453 * Works with domtemplates roo-name="name"
14457 * @cfg {String} selectedClass The css class to add to selected nodes
14459 selectedClass : "x-view-selected",
14461 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14466 * @cfg {String} text to display on mask (default Loading)
14470 * @cfg {Boolean} multiSelect Allow multiple selection
14472 multiSelect : false,
14474 * @cfg {Boolean} singleSelect Allow single selection
14476 singleSelect: false,
14479 * @cfg {Boolean} toggleSelect - selecting
14481 toggleSelect : false,
14484 * @cfg {Boolean} tickable - selecting
14489 * Returns the element this view is bound to.
14490 * @return {Roo.Element}
14492 getEl : function(){
14493 return this.wrapEl;
14499 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14501 refresh : function(){
14502 //Roo.log('refresh');
14505 // if we are using something like 'domtemplate', then
14506 // the what gets used is:
14507 // t.applySubtemplate(NAME, data, wrapping data..)
14508 // the outer template then get' applied with
14509 // the store 'extra data'
14510 // and the body get's added to the
14511 // roo-name="data" node?
14512 // <span class='roo-tpl-{name}'></span> ?????
14516 this.clearSelections();
14517 this.el.update("");
14519 var records = this.store.getRange();
14520 if(records.length < 1) {
14522 // is this valid?? = should it render a template??
14524 this.el.update(this.emptyText);
14528 if (this.dataName) {
14529 this.el.update(t.apply(this.store.meta)); //????
14530 el = this.el.child('.roo-tpl-' + this.dataName);
14533 for(var i = 0, len = records.length; i < len; i++){
14534 var data = this.prepareData(records[i].data, i, records[i]);
14535 this.fireEvent("preparedata", this, data, i, records[i]);
14537 var d = Roo.apply({}, data);
14540 Roo.apply(d, {'roo-id' : Roo.id()});
14544 Roo.each(this.parent.item, function(item){
14545 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14548 Roo.apply(d, {'roo-data-checked' : 'checked'});
14552 html[html.length] = Roo.util.Format.trim(
14554 t.applySubtemplate(this.dataName, d, this.store.meta) :
14561 el.update(html.join(""));
14562 this.nodes = el.dom.childNodes;
14563 this.updateIndexes(0);
14568 * Function to override to reformat the data that is sent to
14569 * the template for each node.
14570 * DEPRICATED - use the preparedata event handler.
14571 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14572 * a JSON object for an UpdateManager bound view).
14574 prepareData : function(data, index, record)
14576 this.fireEvent("preparedata", this, data, index, record);
14580 onUpdate : function(ds, record){
14581 // Roo.log('on update');
14582 this.clearSelections();
14583 var index = this.store.indexOf(record);
14584 var n = this.nodes[index];
14585 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14586 n.parentNode.removeChild(n);
14587 this.updateIndexes(index, index);
14593 onAdd : function(ds, records, index)
14595 //Roo.log(['on Add', ds, records, index] );
14596 this.clearSelections();
14597 if(this.nodes.length == 0){
14601 var n = this.nodes[index];
14602 for(var i = 0, len = records.length; i < len; i++){
14603 var d = this.prepareData(records[i].data, i, records[i]);
14605 this.tpl.insertBefore(n, d);
14608 this.tpl.append(this.el, d);
14611 this.updateIndexes(index);
14614 onRemove : function(ds, record, index){
14615 // Roo.log('onRemove');
14616 this.clearSelections();
14617 var el = this.dataName ?
14618 this.el.child('.roo-tpl-' + this.dataName) :
14621 el.dom.removeChild(this.nodes[index]);
14622 this.updateIndexes(index);
14626 * Refresh an individual node.
14627 * @param {Number} index
14629 refreshNode : function(index){
14630 this.onUpdate(this.store, this.store.getAt(index));
14633 updateIndexes : function(startIndex, endIndex){
14634 var ns = this.nodes;
14635 startIndex = startIndex || 0;
14636 endIndex = endIndex || ns.length - 1;
14637 for(var i = startIndex; i <= endIndex; i++){
14638 ns[i].nodeIndex = i;
14643 * Changes the data store this view uses and refresh the view.
14644 * @param {Store} store
14646 setStore : function(store, initial){
14647 if(!initial && this.store){
14648 this.store.un("datachanged", this.refresh);
14649 this.store.un("add", this.onAdd);
14650 this.store.un("remove", this.onRemove);
14651 this.store.un("update", this.onUpdate);
14652 this.store.un("clear", this.refresh);
14653 this.store.un("beforeload", this.onBeforeLoad);
14654 this.store.un("load", this.onLoad);
14655 this.store.un("loadexception", this.onLoad);
14659 store.on("datachanged", this.refresh, this);
14660 store.on("add", this.onAdd, this);
14661 store.on("remove", this.onRemove, this);
14662 store.on("update", this.onUpdate, this);
14663 store.on("clear", this.refresh, this);
14664 store.on("beforeload", this.onBeforeLoad, this);
14665 store.on("load", this.onLoad, this);
14666 store.on("loadexception", this.onLoad, this);
14674 * onbeforeLoad - masks the loading area.
14677 onBeforeLoad : function(store,opts)
14679 //Roo.log('onBeforeLoad');
14681 this.el.update("");
14683 this.el.mask(this.mask ? this.mask : "Loading" );
14685 onLoad : function ()
14692 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14693 * @param {HTMLElement} node
14694 * @return {HTMLElement} The template node
14696 findItemFromChild : function(node){
14697 var el = this.dataName ?
14698 this.el.child('.roo-tpl-' + this.dataName,true) :
14701 if(!node || node.parentNode == el){
14704 var p = node.parentNode;
14705 while(p && p != el){
14706 if(p.parentNode == el){
14715 onClick : function(e){
14716 var item = this.findItemFromChild(e.getTarget());
14718 var index = this.indexOf(item);
14719 if(this.onItemClick(item, index, e) !== false){
14720 this.fireEvent("click", this, index, item, e);
14723 this.clearSelections();
14728 onContextMenu : function(e){
14729 var item = this.findItemFromChild(e.getTarget());
14731 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14736 onDblClick : function(e){
14737 var item = this.findItemFromChild(e.getTarget());
14739 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14743 onItemClick : function(item, index, e)
14745 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14748 if (this.toggleSelect) {
14749 var m = this.isSelected(item) ? 'unselect' : 'select';
14752 _t[m](item, true, false);
14755 if(this.multiSelect || this.singleSelect){
14756 if(this.multiSelect && e.shiftKey && this.lastSelection){
14757 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14759 this.select(item, this.multiSelect && e.ctrlKey);
14760 this.lastSelection = item;
14763 if(!this.tickable){
14764 e.preventDefault();
14772 * Get the number of selected nodes.
14775 getSelectionCount : function(){
14776 return this.selections.length;
14780 * Get the currently selected nodes.
14781 * @return {Array} An array of HTMLElements
14783 getSelectedNodes : function(){
14784 return this.selections;
14788 * Get the indexes of the selected nodes.
14791 getSelectedIndexes : function(){
14792 var indexes = [], s = this.selections;
14793 for(var i = 0, len = s.length; i < len; i++){
14794 indexes.push(s[i].nodeIndex);
14800 * Clear all selections
14801 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14803 clearSelections : function(suppressEvent){
14804 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14805 this.cmp.elements = this.selections;
14806 this.cmp.removeClass(this.selectedClass);
14807 this.selections = [];
14808 if(!suppressEvent){
14809 this.fireEvent("selectionchange", this, this.selections);
14815 * Returns true if the passed node is selected
14816 * @param {HTMLElement/Number} node The node or node index
14817 * @return {Boolean}
14819 isSelected : function(node){
14820 var s = this.selections;
14824 node = this.getNode(node);
14825 return s.indexOf(node) !== -1;
14830 * @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
14831 * @param {Boolean} keepExisting (optional) true to keep existing selections
14832 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14834 select : function(nodeInfo, keepExisting, suppressEvent){
14835 if(nodeInfo instanceof Array){
14837 this.clearSelections(true);
14839 for(var i = 0, len = nodeInfo.length; i < len; i++){
14840 this.select(nodeInfo[i], true, true);
14844 var node = this.getNode(nodeInfo);
14845 if(!node || this.isSelected(node)){
14846 return; // already selected.
14849 this.clearSelections(true);
14852 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14853 Roo.fly(node).addClass(this.selectedClass);
14854 this.selections.push(node);
14855 if(!suppressEvent){
14856 this.fireEvent("selectionchange", this, this.selections);
14864 * @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
14865 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14866 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14868 unselect : function(nodeInfo, keepExisting, suppressEvent)
14870 if(nodeInfo instanceof Array){
14871 Roo.each(this.selections, function(s) {
14872 this.unselect(s, nodeInfo);
14876 var node = this.getNode(nodeInfo);
14877 if(!node || !this.isSelected(node)){
14878 //Roo.log("not selected");
14879 return; // not selected.
14883 Roo.each(this.selections, function(s) {
14885 Roo.fly(node).removeClass(this.selectedClass);
14892 this.selections= ns;
14893 this.fireEvent("selectionchange", this, this.selections);
14897 * Gets a template node.
14898 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14899 * @return {HTMLElement} The node or null if it wasn't found
14901 getNode : function(nodeInfo){
14902 if(typeof nodeInfo == "string"){
14903 return document.getElementById(nodeInfo);
14904 }else if(typeof nodeInfo == "number"){
14905 return this.nodes[nodeInfo];
14911 * Gets a range template nodes.
14912 * @param {Number} startIndex
14913 * @param {Number} endIndex
14914 * @return {Array} An array of nodes
14916 getNodes : function(start, end){
14917 var ns = this.nodes;
14918 start = start || 0;
14919 end = typeof end == "undefined" ? ns.length - 1 : end;
14922 for(var i = start; i <= end; i++){
14926 for(var i = start; i >= end; i--){
14934 * Finds the index of the passed node
14935 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14936 * @return {Number} The index of the node or -1
14938 indexOf : function(node){
14939 node = this.getNode(node);
14940 if(typeof node.nodeIndex == "number"){
14941 return node.nodeIndex;
14943 var ns = this.nodes;
14944 for(var i = 0, len = ns.length; i < len; i++){
14955 * based on jquery fullcalendar
14959 Roo.bootstrap = Roo.bootstrap || {};
14961 * @class Roo.bootstrap.Calendar
14962 * @extends Roo.bootstrap.Component
14963 * Bootstrap Calendar class
14964 * @cfg {Boolean} loadMask (true|false) default false
14965 * @cfg {Object} header generate the user specific header of the calendar, default false
14968 * Create a new Container
14969 * @param {Object} config The config object
14974 Roo.bootstrap.Calendar = function(config){
14975 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14979 * Fires when a date is selected
14980 * @param {DatePicker} this
14981 * @param {Date} date The selected date
14985 * @event monthchange
14986 * Fires when the displayed month changes
14987 * @param {DatePicker} this
14988 * @param {Date} date The selected month
14990 'monthchange': true,
14992 * @event evententer
14993 * Fires when mouse over an event
14994 * @param {Calendar} this
14995 * @param {event} Event
14997 'evententer': true,
14999 * @event eventleave
15000 * Fires when the mouse leaves an
15001 * @param {Calendar} this
15004 'eventleave': true,
15006 * @event eventclick
15007 * Fires when the mouse click an
15008 * @param {Calendar} this
15017 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15020 * @cfg {Number} startDay
15021 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15029 getAutoCreate : function(){
15032 var fc_button = function(name, corner, style, content ) {
15033 return Roo.apply({},{
15035 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15037 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15040 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15051 style : 'width:100%',
15058 cls : 'fc-header-left',
15060 fc_button('prev', 'left', 'arrow', '‹' ),
15061 fc_button('next', 'right', 'arrow', '›' ),
15062 { tag: 'span', cls: 'fc-header-space' },
15063 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15071 cls : 'fc-header-center',
15075 cls: 'fc-header-title',
15078 html : 'month / year'
15086 cls : 'fc-header-right',
15088 /* fc_button('month', 'left', '', 'month' ),
15089 fc_button('week', '', '', 'week' ),
15090 fc_button('day', 'right', '', 'day' )
15102 header = this.header;
15105 var cal_heads = function() {
15107 // fixme - handle this.
15109 for (var i =0; i < Date.dayNames.length; i++) {
15110 var d = Date.dayNames[i];
15113 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15114 html : d.substring(0,3)
15118 ret[0].cls += ' fc-first';
15119 ret[6].cls += ' fc-last';
15122 var cal_cell = function(n) {
15125 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15130 cls: 'fc-day-number',
15134 cls: 'fc-day-content',
15138 style: 'position: relative;' // height: 17px;
15150 var cal_rows = function() {
15153 for (var r = 0; r < 6; r++) {
15160 for (var i =0; i < Date.dayNames.length; i++) {
15161 var d = Date.dayNames[i];
15162 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15165 row.cn[0].cls+=' fc-first';
15166 row.cn[0].cn[0].style = 'min-height:90px';
15167 row.cn[6].cls+=' fc-last';
15171 ret[0].cls += ' fc-first';
15172 ret[4].cls += ' fc-prev-last';
15173 ret[5].cls += ' fc-last';
15180 cls: 'fc-border-separate',
15181 style : 'width:100%',
15189 cls : 'fc-first fc-last',
15207 cls : 'fc-content',
15208 style : "position: relative;",
15211 cls : 'fc-view fc-view-month fc-grid',
15212 style : 'position: relative',
15213 unselectable : 'on',
15216 cls : 'fc-event-container',
15217 style : 'position:absolute;z-index:8;top:0;left:0;'
15235 initEvents : function()
15238 throw "can not find store for calendar";
15244 style: "text-align:center",
15248 style: "background-color:white;width:50%;margin:250 auto",
15252 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15263 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15265 var size = this.el.select('.fc-content', true).first().getSize();
15266 this.maskEl.setSize(size.width, size.height);
15267 this.maskEl.enableDisplayMode("block");
15268 if(!this.loadMask){
15269 this.maskEl.hide();
15272 this.store = Roo.factory(this.store, Roo.data);
15273 this.store.on('load', this.onLoad, this);
15274 this.store.on('beforeload', this.onBeforeLoad, this);
15278 this.cells = this.el.select('.fc-day',true);
15279 //Roo.log(this.cells);
15280 this.textNodes = this.el.query('.fc-day-number');
15281 this.cells.addClassOnOver('fc-state-hover');
15283 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15284 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15285 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15286 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15288 this.on('monthchange', this.onMonthChange, this);
15290 this.update(new Date().clearTime());
15293 resize : function() {
15294 var sz = this.el.getSize();
15296 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15297 this.el.select('.fc-day-content div',true).setHeight(34);
15302 showPrevMonth : function(e){
15303 this.update(this.activeDate.add("mo", -1));
15305 showToday : function(e){
15306 this.update(new Date().clearTime());
15309 showNextMonth : function(e){
15310 this.update(this.activeDate.add("mo", 1));
15314 showPrevYear : function(){
15315 this.update(this.activeDate.add("y", -1));
15319 showNextYear : function(){
15320 this.update(this.activeDate.add("y", 1));
15325 update : function(date)
15327 var vd = this.activeDate;
15328 this.activeDate = date;
15329 // if(vd && this.el){
15330 // var t = date.getTime();
15331 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15332 // Roo.log('using add remove');
15334 // this.fireEvent('monthchange', this, date);
15336 // this.cells.removeClass("fc-state-highlight");
15337 // this.cells.each(function(c){
15338 // if(c.dateValue == t){
15339 // c.addClass("fc-state-highlight");
15340 // setTimeout(function(){
15341 // try{c.dom.firstChild.focus();}catch(e){}
15351 var days = date.getDaysInMonth();
15353 var firstOfMonth = date.getFirstDateOfMonth();
15354 var startingPos = firstOfMonth.getDay()-this.startDay;
15356 if(startingPos < this.startDay){
15360 var pm = date.add(Date.MONTH, -1);
15361 var prevStart = pm.getDaysInMonth()-startingPos;
15363 this.cells = this.el.select('.fc-day',true);
15364 this.textNodes = this.el.query('.fc-day-number');
15365 this.cells.addClassOnOver('fc-state-hover');
15367 var cells = this.cells.elements;
15368 var textEls = this.textNodes;
15370 Roo.each(cells, function(cell){
15371 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15374 days += startingPos;
15376 // convert everything to numbers so it's fast
15377 var day = 86400000;
15378 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15381 //Roo.log(prevStart);
15383 var today = new Date().clearTime().getTime();
15384 var sel = date.clearTime().getTime();
15385 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15386 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15387 var ddMatch = this.disabledDatesRE;
15388 var ddText = this.disabledDatesText;
15389 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15390 var ddaysText = this.disabledDaysText;
15391 var format = this.format;
15393 var setCellClass = function(cal, cell){
15397 //Roo.log('set Cell Class');
15399 var t = d.getTime();
15403 cell.dateValue = t;
15405 cell.className += " fc-today";
15406 cell.className += " fc-state-highlight";
15407 cell.title = cal.todayText;
15410 // disable highlight in other month..
15411 //cell.className += " fc-state-highlight";
15416 cell.className = " fc-state-disabled";
15417 cell.title = cal.minText;
15421 cell.className = " fc-state-disabled";
15422 cell.title = cal.maxText;
15426 if(ddays.indexOf(d.getDay()) != -1){
15427 cell.title = ddaysText;
15428 cell.className = " fc-state-disabled";
15431 if(ddMatch && format){
15432 var fvalue = d.dateFormat(format);
15433 if(ddMatch.test(fvalue)){
15434 cell.title = ddText.replace("%0", fvalue);
15435 cell.className = " fc-state-disabled";
15439 if (!cell.initialClassName) {
15440 cell.initialClassName = cell.dom.className;
15443 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15448 for(; i < startingPos; i++) {
15449 textEls[i].innerHTML = (++prevStart);
15450 d.setDate(d.getDate()+1);
15452 cells[i].className = "fc-past fc-other-month";
15453 setCellClass(this, cells[i]);
15458 for(; i < days; i++){
15459 intDay = i - startingPos + 1;
15460 textEls[i].innerHTML = (intDay);
15461 d.setDate(d.getDate()+1);
15463 cells[i].className = ''; // "x-date-active";
15464 setCellClass(this, cells[i]);
15468 for(; i < 42; i++) {
15469 textEls[i].innerHTML = (++extraDays);
15470 d.setDate(d.getDate()+1);
15472 cells[i].className = "fc-future fc-other-month";
15473 setCellClass(this, cells[i]);
15476 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15478 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15480 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15481 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15483 if(totalRows != 6){
15484 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15485 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15488 this.fireEvent('monthchange', this, date);
15492 if(!this.internalRender){
15493 var main = this.el.dom.firstChild;
15494 var w = main.offsetWidth;
15495 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15496 Roo.fly(main).setWidth(w);
15497 this.internalRender = true;
15498 // opera does not respect the auto grow header center column
15499 // then, after it gets a width opera refuses to recalculate
15500 // without a second pass
15501 if(Roo.isOpera && !this.secondPass){
15502 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15503 this.secondPass = true;
15504 this.update.defer(10, this, [date]);
15511 findCell : function(dt) {
15512 dt = dt.clearTime().getTime();
15514 this.cells.each(function(c){
15515 //Roo.log("check " +c.dateValue + '?=' + dt);
15516 if(c.dateValue == dt){
15526 findCells : function(ev) {
15527 var s = ev.start.clone().clearTime().getTime();
15529 var e= ev.end.clone().clearTime().getTime();
15532 this.cells.each(function(c){
15533 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15535 if(c.dateValue > e){
15538 if(c.dateValue < s){
15547 // findBestRow: function(cells)
15551 // for (var i =0 ; i < cells.length;i++) {
15552 // ret = Math.max(cells[i].rows || 0,ret);
15559 addItem : function(ev)
15561 // look for vertical location slot in
15562 var cells = this.findCells(ev);
15564 // ev.row = this.findBestRow(cells);
15566 // work out the location.
15570 for(var i =0; i < cells.length; i++) {
15572 cells[i].row = cells[0].row;
15575 cells[i].row = cells[i].row + 1;
15585 if (crow.start.getY() == cells[i].getY()) {
15587 crow.end = cells[i];
15604 cells[0].events.push(ev);
15606 this.calevents.push(ev);
15609 clearEvents: function() {
15611 if(!this.calevents){
15615 Roo.each(this.cells.elements, function(c){
15621 Roo.each(this.calevents, function(e) {
15622 Roo.each(e.els, function(el) {
15623 el.un('mouseenter' ,this.onEventEnter, this);
15624 el.un('mouseleave' ,this.onEventLeave, this);
15629 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15635 renderEvents: function()
15639 this.cells.each(function(c) {
15648 if(c.row != c.events.length){
15649 r = 4 - (4 - (c.row - c.events.length));
15652 c.events = ev.slice(0, r);
15653 c.more = ev.slice(r);
15655 if(c.more.length && c.more.length == 1){
15656 c.events.push(c.more.pop());
15659 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15663 this.cells.each(function(c) {
15665 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15668 for (var e = 0; e < c.events.length; e++){
15669 var ev = c.events[e];
15670 var rows = ev.rows;
15672 for(var i = 0; i < rows.length; i++) {
15674 // how many rows should it span..
15677 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15678 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15680 unselectable : "on",
15683 cls: 'fc-event-inner',
15687 // cls: 'fc-event-time',
15688 // html : cells.length > 1 ? '' : ev.time
15692 cls: 'fc-event-title',
15693 html : String.format('{0}', ev.title)
15700 cls: 'ui-resizable-handle ui-resizable-e',
15701 html : '  '
15708 cfg.cls += ' fc-event-start';
15710 if ((i+1) == rows.length) {
15711 cfg.cls += ' fc-event-end';
15714 var ctr = _this.el.select('.fc-event-container',true).first();
15715 var cg = ctr.createChild(cfg);
15717 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15718 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15720 var r = (c.more.length) ? 1 : 0;
15721 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15722 cg.setWidth(ebox.right - sbox.x -2);
15724 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15725 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15726 cg.on('click', _this.onEventClick, _this, ev);
15737 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15738 style : 'position: absolute',
15739 unselectable : "on",
15742 cls: 'fc-event-inner',
15746 cls: 'fc-event-title',
15754 cls: 'ui-resizable-handle ui-resizable-e',
15755 html : '  '
15761 var ctr = _this.el.select('.fc-event-container',true).first();
15762 var cg = ctr.createChild(cfg);
15764 var sbox = c.select('.fc-day-content',true).first().getBox();
15765 var ebox = c.select('.fc-day-content',true).first().getBox();
15767 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15768 cg.setWidth(ebox.right - sbox.x -2);
15770 cg.on('click', _this.onMoreEventClick, _this, c.more);
15780 onEventEnter: function (e, el,event,d) {
15781 this.fireEvent('evententer', this, el, event);
15784 onEventLeave: function (e, el,event,d) {
15785 this.fireEvent('eventleave', this, el, event);
15788 onEventClick: function (e, el,event,d) {
15789 this.fireEvent('eventclick', this, el, event);
15792 onMonthChange: function () {
15796 onMoreEventClick: function(e, el, more)
15800 this.calpopover.placement = 'right';
15801 this.calpopover.setTitle('More');
15803 this.calpopover.setContent('');
15805 var ctr = this.calpopover.el.select('.popover-content', true).first();
15807 Roo.each(more, function(m){
15809 cls : 'fc-event-hori fc-event-draggable',
15812 var cg = ctr.createChild(cfg);
15814 cg.on('click', _this.onEventClick, _this, m);
15817 this.calpopover.show(el);
15822 onLoad: function ()
15824 this.calevents = [];
15827 if(this.store.getCount() > 0){
15828 this.store.data.each(function(d){
15831 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15832 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15833 time : d.data.start_time,
15834 title : d.data.title,
15835 description : d.data.description,
15836 venue : d.data.venue
15841 this.renderEvents();
15843 if(this.calevents.length && this.loadMask){
15844 this.maskEl.hide();
15848 onBeforeLoad: function()
15850 this.clearEvents();
15852 this.maskEl.show();
15866 * @class Roo.bootstrap.Popover
15867 * @extends Roo.bootstrap.Component
15868 * Bootstrap Popover class
15869 * @cfg {String} html contents of the popover (or false to use children..)
15870 * @cfg {String} title of popover (or false to hide)
15871 * @cfg {String} placement how it is placed
15872 * @cfg {String} trigger click || hover (or false to trigger manually)
15873 * @cfg {String} over what (parent or false to trigger manually.)
15874 * @cfg {Number} delay - delay before showing
15877 * Create a new Popover
15878 * @param {Object} config The config object
15881 Roo.bootstrap.Popover = function(config){
15882 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15888 * After the popover show
15890 * @param {Roo.bootstrap.Popover} this
15895 * After the popover hide
15897 * @param {Roo.bootstrap.Popover} this
15903 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15905 title: 'Fill in a title',
15908 placement : 'right',
15909 trigger : 'hover', // hover
15915 can_build_overlaid : false,
15917 getChildContainer : function()
15919 return this.el.select('.popover-content',true).first();
15922 getAutoCreate : function(){
15925 cls : 'popover roo-dynamic',
15926 style: 'display:block',
15932 cls : 'popover-inner',
15936 cls: 'popover-title',
15940 cls : 'popover-content',
15951 setTitle: function(str)
15954 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15956 setContent: function(str)
15959 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15961 // as it get's added to the bottom of the page.
15962 onRender : function(ct, position)
15964 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15966 var cfg = Roo.apply({}, this.getAutoCreate());
15970 cfg.cls += ' ' + this.cls;
15973 cfg.style = this.style;
15975 //Roo.log("adding to ");
15976 this.el = Roo.get(document.body).createChild(cfg, position);
15977 // Roo.log(this.el);
15982 initEvents : function()
15984 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15985 this.el.enableDisplayMode('block');
15987 if (this.over === false) {
15990 if (this.triggers === false) {
15993 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15994 var triggers = this.trigger ? this.trigger.split(' ') : [];
15995 Roo.each(triggers, function(trigger) {
15997 if (trigger == 'click') {
15998 on_el.on('click', this.toggle, this);
15999 } else if (trigger != 'manual') {
16000 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16001 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16003 on_el.on(eventIn ,this.enter, this);
16004 on_el.on(eventOut, this.leave, this);
16015 toggle : function () {
16016 this.hoverState == 'in' ? this.leave() : this.enter();
16019 enter : function () {
16021 clearTimeout(this.timeout);
16023 this.hoverState = 'in';
16025 if (!this.delay || !this.delay.show) {
16030 this.timeout = setTimeout(function () {
16031 if (_t.hoverState == 'in') {
16034 }, this.delay.show)
16037 leave : function() {
16038 clearTimeout(this.timeout);
16040 this.hoverState = 'out';
16042 if (!this.delay || !this.delay.hide) {
16047 this.timeout = setTimeout(function () {
16048 if (_t.hoverState == 'out') {
16051 }, this.delay.hide)
16054 show : function (on_el)
16057 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16061 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16062 if (this.html !== false) {
16063 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16065 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16066 if (!this.title.length) {
16067 this.el.select('.popover-title',true).hide();
16070 var placement = typeof this.placement == 'function' ?
16071 this.placement.call(this, this.el, on_el) :
16074 var autoToken = /\s?auto?\s?/i;
16075 var autoPlace = autoToken.test(placement);
16077 placement = placement.replace(autoToken, '') || 'top';
16081 //this.el.setXY([0,0]);
16083 this.el.dom.style.display='block';
16084 this.el.addClass(placement);
16086 //this.el.appendTo(on_el);
16088 var p = this.getPosition();
16089 var box = this.el.getBox();
16094 var align = Roo.bootstrap.Popover.alignment[placement];
16095 this.el.alignTo(on_el, align[0],align[1]);
16096 //var arrow = this.el.select('.arrow',true).first();
16097 //arrow.set(align[2],
16099 this.el.addClass('in');
16102 if (this.el.hasClass('fade')) {
16106 this.hoverState = 'in';
16108 this.fireEvent('show', this);
16113 this.el.setXY([0,0]);
16114 this.el.removeClass('in');
16116 this.hoverState = null;
16118 this.fireEvent('hide', this);
16123 Roo.bootstrap.Popover.alignment = {
16124 'left' : ['r-l', [-10,0], 'right'],
16125 'right' : ['l-r', [10,0], 'left'],
16126 'bottom' : ['t-b', [0,10], 'top'],
16127 'top' : [ 'b-t', [0,-10], 'bottom']
16138 * @class Roo.bootstrap.Progress
16139 * @extends Roo.bootstrap.Component
16140 * Bootstrap Progress class
16141 * @cfg {Boolean} striped striped of the progress bar
16142 * @cfg {Boolean} active animated of the progress bar
16146 * Create a new Progress
16147 * @param {Object} config The config object
16150 Roo.bootstrap.Progress = function(config){
16151 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16154 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16159 getAutoCreate : function(){
16167 cfg.cls += ' progress-striped';
16171 cfg.cls += ' active';
16190 * @class Roo.bootstrap.ProgressBar
16191 * @extends Roo.bootstrap.Component
16192 * Bootstrap ProgressBar class
16193 * @cfg {Number} aria_valuenow aria-value now
16194 * @cfg {Number} aria_valuemin aria-value min
16195 * @cfg {Number} aria_valuemax aria-value max
16196 * @cfg {String} label label for the progress bar
16197 * @cfg {String} panel (success | info | warning | danger )
16198 * @cfg {String} role role of the progress bar
16199 * @cfg {String} sr_only text
16203 * Create a new ProgressBar
16204 * @param {Object} config The config object
16207 Roo.bootstrap.ProgressBar = function(config){
16208 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16211 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16215 aria_valuemax : 100,
16221 getAutoCreate : function()
16226 cls: 'progress-bar',
16227 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16239 cfg.role = this.role;
16242 if(this.aria_valuenow){
16243 cfg['aria-valuenow'] = this.aria_valuenow;
16246 if(this.aria_valuemin){
16247 cfg['aria-valuemin'] = this.aria_valuemin;
16250 if(this.aria_valuemax){
16251 cfg['aria-valuemax'] = this.aria_valuemax;
16254 if(this.label && !this.sr_only){
16255 cfg.html = this.label;
16259 cfg.cls += ' progress-bar-' + this.panel;
16265 update : function(aria_valuenow)
16267 this.aria_valuenow = aria_valuenow;
16269 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16284 * @class Roo.bootstrap.TabGroup
16285 * @extends Roo.bootstrap.Column
16286 * Bootstrap Column class
16287 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16288 * @cfg {Boolean} carousel true to make the group behave like a carousel
16289 * @cfg {Boolean} bullets show bullets for the panels
16290 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16291 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16292 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16295 * Create a new TabGroup
16296 * @param {Object} config The config object
16299 Roo.bootstrap.TabGroup = function(config){
16300 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16302 this.navId = Roo.id();
16305 Roo.bootstrap.TabGroup.register(this);
16309 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16312 transition : false,
16317 slideOnTouch : false,
16319 getAutoCreate : function()
16321 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16323 cfg.cls += ' tab-content';
16325 if (this.carousel) {
16326 cfg.cls += ' carousel slide';
16329 cls : 'carousel-inner'
16332 if(this.bullets && !Roo.isTouch){
16335 cls : 'carousel-bullets',
16339 if(this.bullets_cls){
16340 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16343 for (var i = 0; i < this.bullets; i++){
16345 cls : 'bullet bullet-' + i
16353 cfg.cn[0].cn = bullets;
16360 initEvents: function()
16362 if(Roo.isTouch && this.slideOnTouch){
16363 this.el.on("touchstart", this.onTouchStart, this);
16366 if(this.autoslide){
16369 this.slideFn = window.setInterval(function() {
16370 _this.showPanelNext();
16376 onTouchStart : function(e, el, o)
16378 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16382 this.showPanelNext();
16385 getChildContainer : function()
16387 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16391 * register a Navigation item
16392 * @param {Roo.bootstrap.NavItem} the navitem to add
16394 register : function(item)
16396 this.tabs.push( item);
16397 item.navId = this.navId; // not really needed..
16402 getActivePanel : function()
16405 Roo.each(this.tabs, function(t) {
16415 getPanelByName : function(n)
16418 Roo.each(this.tabs, function(t) {
16419 if (t.tabId == n) {
16427 indexOfPanel : function(p)
16430 Roo.each(this.tabs, function(t,i) {
16431 if (t.tabId == p.tabId) {
16440 * show a specific panel
16441 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16442 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16444 showPanel : function (pan)
16446 if(this.transition || typeof(pan) == 'undefined'){
16447 Roo.log("waiting for the transitionend");
16451 if (typeof(pan) == 'number') {
16452 pan = this.tabs[pan];
16455 if (typeof(pan) == 'string') {
16456 pan = this.getPanelByName(pan);
16459 var cur = this.getActivePanel();
16462 Roo.log('pan or acitve pan is undefined');
16466 if (pan.tabId == this.getActivePanel().tabId) {
16470 if (false === cur.fireEvent('beforedeactivate')) {
16474 if(this.bullets > 0 && !Roo.isTouch){
16475 this.setActiveBullet(this.indexOfPanel(pan));
16478 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16480 this.transition = true;
16481 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16482 var lr = dir == 'next' ? 'left' : 'right';
16483 pan.el.addClass(dir); // or prev
16484 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16485 cur.el.addClass(lr); // or right
16486 pan.el.addClass(lr);
16489 cur.el.on('transitionend', function() {
16490 Roo.log("trans end?");
16492 pan.el.removeClass([lr,dir]);
16493 pan.setActive(true);
16495 cur.el.removeClass([lr]);
16496 cur.setActive(false);
16498 _this.transition = false;
16500 }, this, { single: true } );
16505 cur.setActive(false);
16506 pan.setActive(true);
16511 showPanelNext : function()
16513 var i = this.indexOfPanel(this.getActivePanel());
16515 if (i >= this.tabs.length - 1 && !this.autoslide) {
16519 if (i >= this.tabs.length - 1 && this.autoslide) {
16523 this.showPanel(this.tabs[i+1]);
16526 showPanelPrev : function()
16528 var i = this.indexOfPanel(this.getActivePanel());
16530 if (i < 1 && !this.autoslide) {
16534 if (i < 1 && this.autoslide) {
16535 i = this.tabs.length;
16538 this.showPanel(this.tabs[i-1]);
16542 addBullet: function()
16544 if(!this.bullets || Roo.isTouch){
16547 var ctr = this.el.select('.carousel-bullets',true).first();
16548 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16549 var bullet = ctr.createChild({
16550 cls : 'bullet bullet-' + i
16551 },ctr.dom.lastChild);
16556 bullet.on('click', (function(e, el, o, ii, t){
16558 e.preventDefault();
16560 this.showPanel(ii);
16562 if(this.autoslide && this.slideFn){
16563 clearInterval(this.slideFn);
16564 this.slideFn = window.setInterval(function() {
16565 _this.showPanelNext();
16569 }).createDelegate(this, [i, bullet], true));
16574 setActiveBullet : function(i)
16580 Roo.each(this.el.select('.bullet', true).elements, function(el){
16581 el.removeClass('selected');
16584 var bullet = this.el.select('.bullet-' + i, true).first();
16590 bullet.addClass('selected');
16601 Roo.apply(Roo.bootstrap.TabGroup, {
16605 * register a Navigation Group
16606 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16608 register : function(navgrp)
16610 this.groups[navgrp.navId] = navgrp;
16614 * fetch a Navigation Group based on the navigation ID
16615 * if one does not exist , it will get created.
16616 * @param {string} the navgroup to add
16617 * @returns {Roo.bootstrap.NavGroup} the navgroup
16619 get: function(navId) {
16620 if (typeof(this.groups[navId]) == 'undefined') {
16621 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16623 return this.groups[navId] ;
16638 * @class Roo.bootstrap.TabPanel
16639 * @extends Roo.bootstrap.Component
16640 * Bootstrap TabPanel class
16641 * @cfg {Boolean} active panel active
16642 * @cfg {String} html panel content
16643 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16644 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16648 * Create a new TabPanel
16649 * @param {Object} config The config object
16652 Roo.bootstrap.TabPanel = function(config){
16653 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16657 * Fires when the active status changes
16658 * @param {Roo.bootstrap.TabPanel} this
16659 * @param {Boolean} state the new state
16664 * @event beforedeactivate
16665 * Fires before a tab is de-activated - can be used to do validation on a form.
16666 * @param {Roo.bootstrap.TabPanel} this
16667 * @return {Boolean} false if there is an error
16670 'beforedeactivate': true
16673 this.tabId = this.tabId || Roo.id();
16677 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16684 getAutoCreate : function(){
16687 // item is needed for carousel - not sure if it has any effect otherwise
16688 cls: 'tab-pane item',
16689 html: this.html || ''
16693 cfg.cls += ' active';
16697 cfg.tabId = this.tabId;
16704 initEvents: function()
16706 var p = this.parent();
16707 this.navId = this.navId || p.navId;
16709 if (typeof(this.navId) != 'undefined') {
16710 // not really needed.. but just in case.. parent should be a NavGroup.
16711 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16715 var i = tg.tabs.length - 1;
16717 if(this.active && tg.bullets > 0 && i < tg.bullets){
16718 tg.setActiveBullet(i);
16725 onRender : function(ct, position)
16727 // Roo.log("Call onRender: " + this.xtype);
16729 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16737 setActive: function(state)
16739 Roo.log("panel - set active " + this.tabId + "=" + state);
16741 this.active = state;
16743 this.el.removeClass('active');
16745 } else if (!this.el.hasClass('active')) {
16746 this.el.addClass('active');
16749 this.fireEvent('changed', this, state);
16766 * @class Roo.bootstrap.DateField
16767 * @extends Roo.bootstrap.Input
16768 * Bootstrap DateField class
16769 * @cfg {Number} weekStart default 0
16770 * @cfg {String} viewMode default empty, (months|years)
16771 * @cfg {String} minViewMode default empty, (months|years)
16772 * @cfg {Number} startDate default -Infinity
16773 * @cfg {Number} endDate default Infinity
16774 * @cfg {Boolean} todayHighlight default false
16775 * @cfg {Boolean} todayBtn default false
16776 * @cfg {Boolean} calendarWeeks default false
16777 * @cfg {Object} daysOfWeekDisabled default empty
16778 * @cfg {Boolean} singleMode default false (true | false)
16780 * @cfg {Boolean} keyboardNavigation default true
16781 * @cfg {String} language default en
16784 * Create a new DateField
16785 * @param {Object} config The config object
16788 Roo.bootstrap.DateField = function(config){
16789 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16793 * Fires when this field show.
16794 * @param {Roo.bootstrap.DateField} this
16795 * @param {Mixed} date The date value
16800 * Fires when this field hide.
16801 * @param {Roo.bootstrap.DateField} this
16802 * @param {Mixed} date The date value
16807 * Fires when select a date.
16808 * @param {Roo.bootstrap.DateField} this
16809 * @param {Mixed} date The date value
16813 * @event beforeselect
16814 * Fires when before select a date.
16815 * @param {Roo.bootstrap.DateField} this
16816 * @param {Mixed} date The date value
16818 beforeselect : true
16822 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16825 * @cfg {String} format
16826 * The default date format string which can be overriden for localization support. The format must be
16827 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16831 * @cfg {String} altFormats
16832 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16833 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16835 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16843 todayHighlight : false,
16849 keyboardNavigation: true,
16851 calendarWeeks: false,
16853 startDate: -Infinity,
16857 daysOfWeekDisabled: [],
16861 singleMode : false,
16863 UTCDate: function()
16865 return new Date(Date.UTC.apply(Date, arguments));
16868 UTCToday: function()
16870 var today = new Date();
16871 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16874 getDate: function() {
16875 var d = this.getUTCDate();
16876 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16879 getUTCDate: function() {
16883 setDate: function(d) {
16884 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16887 setUTCDate: function(d) {
16889 this.setValue(this.formatDate(this.date));
16892 onRender: function(ct, position)
16895 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16897 this.language = this.language || 'en';
16898 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16899 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16901 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16902 this.format = this.format || 'm/d/y';
16903 this.isInline = false;
16904 this.isInput = true;
16905 this.component = this.el.select('.add-on', true).first() || false;
16906 this.component = (this.component && this.component.length === 0) ? false : this.component;
16907 this.hasInput = this.component && this.inputEL().length;
16909 if (typeof(this.minViewMode === 'string')) {
16910 switch (this.minViewMode) {
16912 this.minViewMode = 1;
16915 this.minViewMode = 2;
16918 this.minViewMode = 0;
16923 if (typeof(this.viewMode === 'string')) {
16924 switch (this.viewMode) {
16937 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16939 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16941 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16943 this.picker().on('mousedown', this.onMousedown, this);
16944 this.picker().on('click', this.onClick, this);
16946 this.picker().addClass('datepicker-dropdown');
16948 this.startViewMode = this.viewMode;
16950 if(this.singleMode){
16951 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16952 v.setVisibilityMode(Roo.Element.DISPLAY);
16956 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16957 v.setStyle('width', '189px');
16961 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16962 if(!this.calendarWeeks){
16967 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16968 v.attr('colspan', function(i, val){
16969 return parseInt(val) + 1;
16974 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16976 this.setStartDate(this.startDate);
16977 this.setEndDate(this.endDate);
16979 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16986 if(this.isInline) {
16991 picker : function()
16993 return this.pickerEl;
16994 // return this.el.select('.datepicker', true).first();
16997 fillDow: function()
16999 var dowCnt = this.weekStart;
17008 if(this.calendarWeeks){
17016 while (dowCnt < this.weekStart + 7) {
17020 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17024 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17027 fillMonths: function()
17030 var months = this.picker().select('>.datepicker-months td', true).first();
17032 months.dom.innerHTML = '';
17038 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17041 months.createChild(month);
17048 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;
17050 if (this.date < this.startDate) {
17051 this.viewDate = new Date(this.startDate);
17052 } else if (this.date > this.endDate) {
17053 this.viewDate = new Date(this.endDate);
17055 this.viewDate = new Date(this.date);
17063 var d = new Date(this.viewDate),
17064 year = d.getUTCFullYear(),
17065 month = d.getUTCMonth(),
17066 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17067 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17068 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17069 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17070 currentDate = this.date && this.date.valueOf(),
17071 today = this.UTCToday();
17073 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17075 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17077 // this.picker.select('>tfoot th.today').
17078 // .text(dates[this.language].today)
17079 // .toggle(this.todayBtn !== false);
17081 this.updateNavArrows();
17084 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17086 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17088 prevMonth.setUTCDate(day);
17090 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17092 var nextMonth = new Date(prevMonth);
17094 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17096 nextMonth = nextMonth.valueOf();
17098 var fillMonths = false;
17100 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17102 while(prevMonth.valueOf() < nextMonth) {
17105 if (prevMonth.getUTCDay() === this.weekStart) {
17107 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17115 if(this.calendarWeeks){
17116 // ISO 8601: First week contains first thursday.
17117 // ISO also states week starts on Monday, but we can be more abstract here.
17119 // Start of current week: based on weekstart/current date
17120 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17121 // Thursday of this week
17122 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17123 // First Thursday of year, year from thursday
17124 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17125 // Calendar week: ms between thursdays, div ms per day, div 7 days
17126 calWeek = (th - yth) / 864e5 / 7 + 1;
17128 fillMonths.cn.push({
17136 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17138 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17141 if (this.todayHighlight &&
17142 prevMonth.getUTCFullYear() == today.getFullYear() &&
17143 prevMonth.getUTCMonth() == today.getMonth() &&
17144 prevMonth.getUTCDate() == today.getDate()) {
17145 clsName += ' today';
17148 if (currentDate && prevMonth.valueOf() === currentDate) {
17149 clsName += ' active';
17152 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17153 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17154 clsName += ' disabled';
17157 fillMonths.cn.push({
17159 cls: 'day ' + clsName,
17160 html: prevMonth.getDate()
17163 prevMonth.setDate(prevMonth.getDate()+1);
17166 var currentYear = this.date && this.date.getUTCFullYear();
17167 var currentMonth = this.date && this.date.getUTCMonth();
17169 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17171 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17172 v.removeClass('active');
17174 if(currentYear === year && k === currentMonth){
17175 v.addClass('active');
17178 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17179 v.addClass('disabled');
17185 year = parseInt(year/10, 10) * 10;
17187 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17189 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17192 for (var i = -1; i < 11; i++) {
17193 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17195 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17203 showMode: function(dir)
17206 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17209 Roo.each(this.picker().select('>div',true).elements, function(v){
17210 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17213 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17218 if(this.isInline) {
17222 this.picker().removeClass(['bottom', 'top']);
17224 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17226 * place to the top of element!
17230 this.picker().addClass('top');
17231 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17236 this.picker().addClass('bottom');
17238 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17241 parseDate : function(value)
17243 if(!value || value instanceof Date){
17246 var v = Date.parseDate(value, this.format);
17247 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17248 v = Date.parseDate(value, 'Y-m-d');
17250 if(!v && this.altFormats){
17251 if(!this.altFormatsArray){
17252 this.altFormatsArray = this.altFormats.split("|");
17254 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17255 v = Date.parseDate(value, this.altFormatsArray[i]);
17261 formatDate : function(date, fmt)
17263 return (!date || !(date instanceof Date)) ?
17264 date : date.dateFormat(fmt || this.format);
17267 onFocus : function()
17269 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17273 onBlur : function()
17275 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17277 var d = this.inputEl().getValue();
17286 this.picker().show();
17290 this.fireEvent('show', this, this.date);
17295 if(this.isInline) {
17298 this.picker().hide();
17299 this.viewMode = this.startViewMode;
17302 this.fireEvent('hide', this, this.date);
17306 onMousedown: function(e)
17308 e.stopPropagation();
17309 e.preventDefault();
17314 Roo.bootstrap.DateField.superclass.keyup.call(this);
17318 setValue: function(v)
17320 if(this.fireEvent('beforeselect', this, v) !== false){
17321 var d = new Date(this.parseDate(v) ).clearTime();
17323 if(isNaN(d.getTime())){
17324 this.date = this.viewDate = '';
17325 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17329 v = this.formatDate(d);
17331 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17333 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17337 this.fireEvent('select', this, this.date);
17341 getValue: function()
17343 return this.formatDate(this.date);
17346 fireKey: function(e)
17348 if (!this.picker().isVisible()){
17349 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17355 var dateChanged = false,
17357 newDate, newViewDate;
17362 e.preventDefault();
17366 if (!this.keyboardNavigation) {
17369 dir = e.keyCode == 37 ? -1 : 1;
17372 newDate = this.moveYear(this.date, dir);
17373 newViewDate = this.moveYear(this.viewDate, dir);
17374 } else if (e.shiftKey){
17375 newDate = this.moveMonth(this.date, dir);
17376 newViewDate = this.moveMonth(this.viewDate, dir);
17378 newDate = new Date(this.date);
17379 newDate.setUTCDate(this.date.getUTCDate() + dir);
17380 newViewDate = new Date(this.viewDate);
17381 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17383 if (this.dateWithinRange(newDate)){
17384 this.date = newDate;
17385 this.viewDate = newViewDate;
17386 this.setValue(this.formatDate(this.date));
17388 e.preventDefault();
17389 dateChanged = true;
17394 if (!this.keyboardNavigation) {
17397 dir = e.keyCode == 38 ? -1 : 1;
17399 newDate = this.moveYear(this.date, dir);
17400 newViewDate = this.moveYear(this.viewDate, dir);
17401 } else if (e.shiftKey){
17402 newDate = this.moveMonth(this.date, dir);
17403 newViewDate = this.moveMonth(this.viewDate, dir);
17405 newDate = new Date(this.date);
17406 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17407 newViewDate = new Date(this.viewDate);
17408 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17410 if (this.dateWithinRange(newDate)){
17411 this.date = newDate;
17412 this.viewDate = newViewDate;
17413 this.setValue(this.formatDate(this.date));
17415 e.preventDefault();
17416 dateChanged = true;
17420 this.setValue(this.formatDate(this.date));
17422 e.preventDefault();
17425 this.setValue(this.formatDate(this.date));
17439 onClick: function(e)
17441 e.stopPropagation();
17442 e.preventDefault();
17444 var target = e.getTarget();
17446 if(target.nodeName.toLowerCase() === 'i'){
17447 target = Roo.get(target).dom.parentNode;
17450 var nodeName = target.nodeName;
17451 var className = target.className;
17452 var html = target.innerHTML;
17453 //Roo.log(nodeName);
17455 switch(nodeName.toLowerCase()) {
17457 switch(className) {
17463 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17464 switch(this.viewMode){
17466 this.viewDate = this.moveMonth(this.viewDate, dir);
17470 this.viewDate = this.moveYear(this.viewDate, dir);
17476 var date = new Date();
17477 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17479 this.setValue(this.formatDate(this.date));
17486 if (className.indexOf('disabled') < 0) {
17487 this.viewDate.setUTCDate(1);
17488 if (className.indexOf('month') > -1) {
17489 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17491 var year = parseInt(html, 10) || 0;
17492 this.viewDate.setUTCFullYear(year);
17496 if(this.singleMode){
17497 this.setValue(this.formatDate(this.viewDate));
17508 //Roo.log(className);
17509 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17510 var day = parseInt(html, 10) || 1;
17511 var year = this.viewDate.getUTCFullYear(),
17512 month = this.viewDate.getUTCMonth();
17514 if (className.indexOf('old') > -1) {
17521 } else if (className.indexOf('new') > -1) {
17529 //Roo.log([year,month,day]);
17530 this.date = this.UTCDate(year, month, day,0,0,0,0);
17531 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17533 //Roo.log(this.formatDate(this.date));
17534 this.setValue(this.formatDate(this.date));
17541 setStartDate: function(startDate)
17543 this.startDate = startDate || -Infinity;
17544 if (this.startDate !== -Infinity) {
17545 this.startDate = this.parseDate(this.startDate);
17548 this.updateNavArrows();
17551 setEndDate: function(endDate)
17553 this.endDate = endDate || Infinity;
17554 if (this.endDate !== Infinity) {
17555 this.endDate = this.parseDate(this.endDate);
17558 this.updateNavArrows();
17561 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17563 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17564 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17565 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17567 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17568 return parseInt(d, 10);
17571 this.updateNavArrows();
17574 updateNavArrows: function()
17576 if(this.singleMode){
17580 var d = new Date(this.viewDate),
17581 year = d.getUTCFullYear(),
17582 month = d.getUTCMonth();
17584 Roo.each(this.picker().select('.prev', true).elements, function(v){
17586 switch (this.viewMode) {
17589 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17595 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17602 Roo.each(this.picker().select('.next', true).elements, function(v){
17604 switch (this.viewMode) {
17607 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17613 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17621 moveMonth: function(date, dir)
17626 var new_date = new Date(date.valueOf()),
17627 day = new_date.getUTCDate(),
17628 month = new_date.getUTCMonth(),
17629 mag = Math.abs(dir),
17631 dir = dir > 0 ? 1 : -1;
17634 // If going back one month, make sure month is not current month
17635 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17637 return new_date.getUTCMonth() == month;
17639 // If going forward one month, make sure month is as expected
17640 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17642 return new_date.getUTCMonth() != new_month;
17644 new_month = month + dir;
17645 new_date.setUTCMonth(new_month);
17646 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17647 if (new_month < 0 || new_month > 11) {
17648 new_month = (new_month + 12) % 12;
17651 // For magnitudes >1, move one month at a time...
17652 for (var i=0; i<mag; i++) {
17653 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17654 new_date = this.moveMonth(new_date, dir);
17656 // ...then reset the day, keeping it in the new month
17657 new_month = new_date.getUTCMonth();
17658 new_date.setUTCDate(day);
17660 return new_month != new_date.getUTCMonth();
17663 // Common date-resetting loop -- if date is beyond end of month, make it
17666 new_date.setUTCDate(--day);
17667 new_date.setUTCMonth(new_month);
17672 moveYear: function(date, dir)
17674 return this.moveMonth(date, dir*12);
17677 dateWithinRange: function(date)
17679 return date >= this.startDate && date <= this.endDate;
17685 this.picker().remove();
17690 Roo.apply(Roo.bootstrap.DateField, {
17701 html: '<i class="fa fa-arrow-left"/>'
17711 html: '<i class="fa fa-arrow-right"/>'
17753 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17754 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17755 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17756 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17757 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17770 navFnc: 'FullYear',
17775 navFnc: 'FullYear',
17780 Roo.apply(Roo.bootstrap.DateField, {
17784 cls: 'datepicker dropdown-menu roo-dynamic',
17788 cls: 'datepicker-days',
17792 cls: 'table-condensed',
17794 Roo.bootstrap.DateField.head,
17798 Roo.bootstrap.DateField.footer
17805 cls: 'datepicker-months',
17809 cls: 'table-condensed',
17811 Roo.bootstrap.DateField.head,
17812 Roo.bootstrap.DateField.content,
17813 Roo.bootstrap.DateField.footer
17820 cls: 'datepicker-years',
17824 cls: 'table-condensed',
17826 Roo.bootstrap.DateField.head,
17827 Roo.bootstrap.DateField.content,
17828 Roo.bootstrap.DateField.footer
17847 * @class Roo.bootstrap.TimeField
17848 * @extends Roo.bootstrap.Input
17849 * Bootstrap DateField class
17853 * Create a new TimeField
17854 * @param {Object} config The config object
17857 Roo.bootstrap.TimeField = function(config){
17858 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17862 * Fires when this field show.
17863 * @param {Roo.bootstrap.DateField} thisthis
17864 * @param {Mixed} date The date value
17869 * Fires when this field hide.
17870 * @param {Roo.bootstrap.DateField} this
17871 * @param {Mixed} date The date value
17876 * Fires when select a date.
17877 * @param {Roo.bootstrap.DateField} this
17878 * @param {Mixed} date The date value
17884 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17887 * @cfg {String} format
17888 * The default time format string which can be overriden for localization support. The format must be
17889 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17893 onRender: function(ct, position)
17896 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17898 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17900 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17902 this.pop = this.picker().select('>.datepicker-time',true).first();
17903 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17905 this.picker().on('mousedown', this.onMousedown, this);
17906 this.picker().on('click', this.onClick, this);
17908 this.picker().addClass('datepicker-dropdown');
17913 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17914 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17915 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17916 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17917 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17918 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17922 fireKey: function(e){
17923 if (!this.picker().isVisible()){
17924 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17930 e.preventDefault();
17938 this.onTogglePeriod();
17941 this.onIncrementMinutes();
17944 this.onDecrementMinutes();
17953 onClick: function(e) {
17954 e.stopPropagation();
17955 e.preventDefault();
17958 picker : function()
17960 return this.el.select('.datepicker', true).first();
17963 fillTime: function()
17965 var time = this.pop.select('tbody', true).first();
17967 time.dom.innerHTML = '';
17982 cls: 'hours-up glyphicon glyphicon-chevron-up'
18002 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18023 cls: 'timepicker-hour',
18038 cls: 'timepicker-minute',
18053 cls: 'btn btn-primary period',
18075 cls: 'hours-down glyphicon glyphicon-chevron-down'
18095 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18113 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18120 var hours = this.time.getHours();
18121 var minutes = this.time.getMinutes();
18134 hours = hours - 12;
18138 hours = '0' + hours;
18142 minutes = '0' + minutes;
18145 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18146 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18147 this.pop.select('button', true).first().dom.innerHTML = period;
18153 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18155 var cls = ['bottom'];
18157 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18164 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18169 this.picker().addClass(cls.join('-'));
18173 Roo.each(cls, function(c){
18175 _this.picker().setTop(_this.inputEl().getHeight());
18179 _this.picker().setTop(0 - _this.picker().getHeight());
18184 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18188 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18195 onFocus : function()
18197 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18201 onBlur : function()
18203 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18209 this.picker().show();
18214 this.fireEvent('show', this, this.date);
18219 this.picker().hide();
18222 this.fireEvent('hide', this, this.date);
18225 setTime : function()
18228 this.setValue(this.time.format(this.format));
18230 this.fireEvent('select', this, this.date);
18235 onMousedown: function(e){
18236 e.stopPropagation();
18237 e.preventDefault();
18240 onIncrementHours: function()
18242 Roo.log('onIncrementHours');
18243 this.time = this.time.add(Date.HOUR, 1);
18248 onDecrementHours: function()
18250 Roo.log('onDecrementHours');
18251 this.time = this.time.add(Date.HOUR, -1);
18255 onIncrementMinutes: function()
18257 Roo.log('onIncrementMinutes');
18258 this.time = this.time.add(Date.MINUTE, 1);
18262 onDecrementMinutes: function()
18264 Roo.log('onDecrementMinutes');
18265 this.time = this.time.add(Date.MINUTE, -1);
18269 onTogglePeriod: function()
18271 Roo.log('onTogglePeriod');
18272 this.time = this.time.add(Date.HOUR, 12);
18279 Roo.apply(Roo.bootstrap.TimeField, {
18309 cls: 'btn btn-info ok',
18321 Roo.apply(Roo.bootstrap.TimeField, {
18325 cls: 'datepicker dropdown-menu',
18329 cls: 'datepicker-time',
18333 cls: 'table-condensed',
18335 Roo.bootstrap.TimeField.content,
18336 Roo.bootstrap.TimeField.footer
18355 * @class Roo.bootstrap.MonthField
18356 * @extends Roo.bootstrap.Input
18357 * Bootstrap MonthField class
18359 * @cfg {String} language default en
18362 * Create a new MonthField
18363 * @param {Object} config The config object
18366 Roo.bootstrap.MonthField = function(config){
18367 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18372 * Fires when this field show.
18373 * @param {Roo.bootstrap.MonthField} this
18374 * @param {Mixed} date The date value
18379 * Fires when this field hide.
18380 * @param {Roo.bootstrap.MonthField} this
18381 * @param {Mixed} date The date value
18386 * Fires when select a date.
18387 * @param {Roo.bootstrap.MonthField} this
18388 * @param {String} oldvalue The old value
18389 * @param {String} newvalue The new value
18395 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18397 onRender: function(ct, position)
18400 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18402 this.language = this.language || 'en';
18403 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18404 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18406 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18407 this.isInline = false;
18408 this.isInput = true;
18409 this.component = this.el.select('.add-on', true).first() || false;
18410 this.component = (this.component && this.component.length === 0) ? false : this.component;
18411 this.hasInput = this.component && this.inputEL().length;
18413 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18415 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18417 this.picker().on('mousedown', this.onMousedown, this);
18418 this.picker().on('click', this.onClick, this);
18420 this.picker().addClass('datepicker-dropdown');
18422 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18423 v.setStyle('width', '189px');
18430 if(this.isInline) {
18436 setValue: function(v, suppressEvent)
18438 var o = this.getValue();
18440 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18444 if(suppressEvent !== true){
18445 this.fireEvent('select', this, o, v);
18450 getValue: function()
18455 onClick: function(e)
18457 e.stopPropagation();
18458 e.preventDefault();
18460 var target = e.getTarget();
18462 if(target.nodeName.toLowerCase() === 'i'){
18463 target = Roo.get(target).dom.parentNode;
18466 var nodeName = target.nodeName;
18467 var className = target.className;
18468 var html = target.innerHTML;
18470 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18474 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18476 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18482 picker : function()
18484 return this.pickerEl;
18487 fillMonths: function()
18490 var months = this.picker().select('>.datepicker-months td', true).first();
18492 months.dom.innerHTML = '';
18498 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18501 months.createChild(month);
18510 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18511 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18514 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18515 e.removeClass('active');
18517 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18518 e.addClass('active');
18525 if(this.isInline) {
18529 this.picker().removeClass(['bottom', 'top']);
18531 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18533 * place to the top of element!
18537 this.picker().addClass('top');
18538 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18543 this.picker().addClass('bottom');
18545 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18548 onFocus : function()
18550 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18554 onBlur : function()
18556 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18558 var d = this.inputEl().getValue();
18567 this.picker().show();
18568 this.picker().select('>.datepicker-months', true).first().show();
18572 this.fireEvent('show', this, this.date);
18577 if(this.isInline) {
18580 this.picker().hide();
18581 this.fireEvent('hide', this, this.date);
18585 onMousedown: function(e)
18587 e.stopPropagation();
18588 e.preventDefault();
18593 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18597 fireKey: function(e)
18599 if (!this.picker().isVisible()){
18600 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18611 e.preventDefault();
18615 dir = e.keyCode == 37 ? -1 : 1;
18617 this.vIndex = this.vIndex + dir;
18619 if(this.vIndex < 0){
18623 if(this.vIndex > 11){
18627 if(isNaN(this.vIndex)){
18631 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18637 dir = e.keyCode == 38 ? -1 : 1;
18639 this.vIndex = this.vIndex + dir * 4;
18641 if(this.vIndex < 0){
18645 if(this.vIndex > 11){
18649 if(isNaN(this.vIndex)){
18653 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18658 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18659 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18663 e.preventDefault();
18666 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18667 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18683 this.picker().remove();
18688 Roo.apply(Roo.bootstrap.MonthField, {
18707 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18708 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18713 Roo.apply(Roo.bootstrap.MonthField, {
18717 cls: 'datepicker dropdown-menu roo-dynamic',
18721 cls: 'datepicker-months',
18725 cls: 'table-condensed',
18727 Roo.bootstrap.DateField.content
18747 * @class Roo.bootstrap.CheckBox
18748 * @extends Roo.bootstrap.Input
18749 * Bootstrap CheckBox class
18751 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18752 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18753 * @cfg {String} boxLabel The text that appears beside the checkbox
18754 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18755 * @cfg {Boolean} checked initnal the element
18756 * @cfg {Boolean} inline inline the element (default false)
18757 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18760 * Create a new CheckBox
18761 * @param {Object} config The config object
18764 Roo.bootstrap.CheckBox = function(config){
18765 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18770 * Fires when the element is checked or unchecked.
18771 * @param {Roo.bootstrap.CheckBox} this This input
18772 * @param {Boolean} checked The new checked value
18779 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18781 inputType: 'checkbox',
18789 getAutoCreate : function()
18791 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18797 cfg.cls = 'form-group ' + this.inputType; //input-group
18800 cfg.cls += ' ' + this.inputType + '-inline';
18806 type : this.inputType,
18807 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18808 cls : 'roo-' + this.inputType, //'form-box',
18809 placeholder : this.placeholder || ''
18813 if (this.weight) { // Validity check?
18814 cfg.cls += " " + this.inputType + "-" + this.weight;
18817 if (this.disabled) {
18818 input.disabled=true;
18822 input.checked = this.checked;
18826 input.name = this.name;
18830 input.cls += ' input-' + this.size;
18835 ['xs','sm','md','lg'].map(function(size){
18836 if (settings[size]) {
18837 cfg.cls += ' col-' + size + '-' + settings[size];
18841 var inputblock = input;
18843 if (this.before || this.after) {
18846 cls : 'input-group',
18851 inputblock.cn.push({
18853 cls : 'input-group-addon',
18858 inputblock.cn.push(input);
18861 inputblock.cn.push({
18863 cls : 'input-group-addon',
18870 if (align ==='left' && this.fieldLabel.length) {
18871 // Roo.log("left and has label");
18877 cls : 'control-label col-md-' + this.labelWidth,
18878 html : this.fieldLabel
18882 cls : "col-md-" + (12 - this.labelWidth),
18889 } else if ( this.fieldLabel.length) {
18890 // Roo.log(" label");
18894 tag: this.boxLabel ? 'span' : 'label',
18896 cls: 'control-label box-input-label',
18897 //cls : 'input-group-addon',
18898 html : this.fieldLabel
18908 // Roo.log(" no label && no align");
18909 cfg.cn = [ inputblock ] ;
18915 var boxLabelCfg = {
18917 //'for': id, // box label is handled by onclick - so no for...
18919 html: this.boxLabel
18923 boxLabelCfg.tooltip = this.tooltip;
18926 cfg.cn.push(boxLabelCfg);
18936 * return the real input element.
18938 inputEl: function ()
18940 return this.el.select('input.roo-' + this.inputType,true).first();
18943 labelEl: function()
18945 return this.el.select('label.control-label',true).first();
18947 /* depricated... */
18951 return this.labelEl();
18954 boxLabelEl: function()
18956 return this.el.select('label.box-label',true).first();
18959 initEvents : function()
18961 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18963 this.inputEl().on('click', this.onClick, this);
18965 if (this.boxLabel) {
18966 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18969 this.startValue = this.getValue();
18972 Roo.bootstrap.CheckBox.register(this);
18976 onClick : function()
18978 this.setChecked(!this.checked);
18981 setChecked : function(state,suppressEvent)
18983 this.startValue = this.getValue();
18985 if(this.inputType == 'radio'){
18987 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18988 e.dom.checked = false;
18991 this.inputEl().dom.checked = true;
18993 this.inputEl().dom.value = this.inputValue;
18995 if(suppressEvent !== true){
18996 this.fireEvent('check', this, true);
19004 this.checked = state;
19006 this.inputEl().dom.checked = state;
19008 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19010 if(suppressEvent !== true){
19011 this.fireEvent('check', this, state);
19017 getValue : function()
19019 if(this.inputType == 'radio'){
19020 return this.getGroupValue();
19023 return this.inputEl().getValue();
19027 getGroupValue : function()
19029 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19033 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19036 setValue : function(v,suppressEvent)
19038 if(this.inputType == 'radio'){
19039 this.setGroupValue(v, suppressEvent);
19043 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19048 setGroupValue : function(v, suppressEvent)
19050 this.startValue = this.getValue();
19052 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19053 e.dom.checked = false;
19055 if(e.dom.value == v){
19056 e.dom.checked = true;
19060 if(suppressEvent !== true){
19061 this.fireEvent('check', this, true);
19069 validate : function()
19073 (this.inputType == 'radio' && this.validateRadio()) ||
19074 (this.inputType == 'checkbox' && this.validateCheckbox())
19080 this.markInvalid();
19084 validateRadio : function()
19088 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19089 if(!e.dom.checked){
19101 validateCheckbox : function()
19104 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19107 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19115 for(var i in group){
19120 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19127 * Mark this field as valid
19129 markValid : function()
19131 if(this.allowBlank){
19137 this.fireEvent('valid', this);
19139 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19142 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19149 if(this.inputType == 'radio'){
19150 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19151 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19152 e.findParent('.form-group', false, true).addClass(_this.validClass);
19159 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19160 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19164 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19170 for(var i in group){
19171 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19172 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19177 * Mark this field as invalid
19178 * @param {String} msg The validation message
19180 markInvalid : function(msg)
19182 if(this.allowBlank){
19188 this.fireEvent('invalid', this, msg);
19190 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19193 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19197 label.markInvalid();
19200 if(this.inputType == 'radio'){
19201 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19202 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19203 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19210 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19211 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19215 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19221 for(var i in group){
19222 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19223 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19230 Roo.apply(Roo.bootstrap.CheckBox, {
19235 * register a CheckBox Group
19236 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19238 register : function(checkbox)
19240 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19241 this.groups[checkbox.groupId] = {};
19244 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19248 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19252 * fetch a CheckBox Group based on the group ID
19253 * @param {string} the group ID
19254 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19256 get: function(groupId) {
19257 if (typeof(this.groups[groupId]) == 'undefined') {
19261 return this.groups[groupId] ;
19273 *<div class="radio">
19275 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19276 Option one is this and that—be sure to include why it's great
19283 *<label class="radio-inline">fieldLabel</label>
19284 *<label class="radio-inline">
19285 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19293 * @class Roo.bootstrap.Radio
19294 * @extends Roo.bootstrap.CheckBox
19295 * Bootstrap Radio class
19298 * Create a new Radio
19299 * @param {Object} config The config object
19302 Roo.bootstrap.Radio = function(config){
19303 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19307 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19309 inputType: 'radio',
19313 getAutoCreate : function()
19315 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19316 align = align || 'left'; // default...
19323 tag : this.inline ? 'span' : 'div',
19328 var inline = this.inline ? ' radio-inline' : '';
19332 // does not need for, as we wrap the input with it..
19334 cls : 'control-label box-label' + inline,
19337 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19341 //cls : 'control-label' + inline,
19342 html : this.fieldLabel,
19343 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19352 type : this.inputType,
19353 //value : (!this.checked) ? this.valueOff : this.inputValue,
19354 value : this.inputValue,
19356 placeholder : this.placeholder || '' // ?? needed????
19359 if (this.weight) { // Validity check?
19360 input.cls += " radio-" + this.weight;
19362 if (this.disabled) {
19363 input.disabled=true;
19367 input.checked = this.checked;
19371 input.name = this.name;
19375 input.cls += ' input-' + this.size;
19378 //?? can span's inline have a width??
19381 ['xs','sm','md','lg'].map(function(size){
19382 if (settings[size]) {
19383 cfg.cls += ' col-' + size + '-' + settings[size];
19387 var inputblock = input;
19389 if (this.before || this.after) {
19392 cls : 'input-group',
19397 inputblock.cn.push({
19399 cls : 'input-group-addon',
19403 inputblock.cn.push(input);
19405 inputblock.cn.push({
19407 cls : 'input-group-addon',
19415 if (this.fieldLabel && this.fieldLabel.length) {
19416 cfg.cn.push(fieldLabel);
19419 // normal bootstrap puts the input inside the label.
19420 // however with our styled version - it has to go after the input.
19422 //lbl.cn.push(inputblock);
19426 cls: 'radio' + inline,
19433 cfg.cn.push( lblwrap);
19438 html: this.boxLabel
19447 initEvents : function()
19449 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19451 this.inputEl().on('click', this.onClick, this);
19452 if (this.boxLabel) {
19453 //Roo.log('find label');
19454 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19459 inputEl: function ()
19461 return this.el.select('input.roo-radio',true).first();
19463 onClick : function()
19466 this.setChecked(true);
19469 setChecked : function(state,suppressEvent)
19472 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19473 v.dom.checked = false;
19476 Roo.log(this.inputEl().dom);
19477 this.checked = state;
19478 this.inputEl().dom.checked = state;
19480 if(suppressEvent !== true){
19481 this.fireEvent('check', this, state);
19484 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19488 getGroupValue : function()
19491 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19492 if(v.dom.checked == true){
19493 value = v.dom.value;
19501 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19502 * @return {Mixed} value The field value
19504 getValue : function(){
19505 return this.getGroupValue();
19511 //<script type="text/javascript">
19514 * Based Ext JS Library 1.1.1
19515 * Copyright(c) 2006-2007, Ext JS, LLC.
19521 * @class Roo.HtmlEditorCore
19522 * @extends Roo.Component
19523 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19525 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19528 Roo.HtmlEditorCore = function(config){
19531 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19536 * @event initialize
19537 * Fires when the editor is fully initialized (including the iframe)
19538 * @param {Roo.HtmlEditorCore} this
19543 * Fires when the editor is first receives the focus. Any insertion must wait
19544 * until after this event.
19545 * @param {Roo.HtmlEditorCore} this
19549 * @event beforesync
19550 * Fires before the textarea is updated with content from the editor iframe. Return false
19551 * to cancel the sync.
19552 * @param {Roo.HtmlEditorCore} this
19553 * @param {String} html
19557 * @event beforepush
19558 * Fires before the iframe editor is updated with content from the textarea. Return false
19559 * to cancel the push.
19560 * @param {Roo.HtmlEditorCore} this
19561 * @param {String} html
19566 * Fires when the textarea is updated with content from the editor iframe.
19567 * @param {Roo.HtmlEditorCore} this
19568 * @param {String} html
19573 * Fires when the iframe editor is updated with content from the textarea.
19574 * @param {Roo.HtmlEditorCore} this
19575 * @param {String} html
19580 * @event editorevent
19581 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19582 * @param {Roo.HtmlEditorCore} this
19588 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19590 // defaults : white / black...
19591 this.applyBlacklists();
19598 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19602 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19608 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19613 * @cfg {Number} height (in pixels)
19617 * @cfg {Number} width (in pixels)
19622 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19625 stylesheets: false,
19630 // private properties
19631 validationEvent : false,
19633 initialized : false,
19635 sourceEditMode : false,
19636 onFocus : Roo.emptyFn,
19638 hideMode:'offsets',
19642 // blacklist + whitelisted elements..
19649 * Protected method that will not generally be called directly. It
19650 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19651 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19653 getDocMarkup : function(){
19657 // inherit styels from page...??
19658 if (this.stylesheets === false) {
19660 Roo.get(document.head).select('style').each(function(node) {
19661 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19664 Roo.get(document.head).select('link').each(function(node) {
19665 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19668 } else if (!this.stylesheets.length) {
19670 st = '<style type="text/css">' +
19671 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19677 st += '<style type="text/css">' +
19678 'IMG { cursor: pointer } ' +
19682 return '<html><head>' + st +
19683 //<style type="text/css">' +
19684 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19686 ' </head><body class="roo-htmleditor-body"></body></html>';
19690 onRender : function(ct, position)
19693 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19694 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19697 this.el.dom.style.border = '0 none';
19698 this.el.dom.setAttribute('tabIndex', -1);
19699 this.el.addClass('x-hidden hide');
19703 if(Roo.isIE){ // fix IE 1px bogus margin
19704 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19708 this.frameId = Roo.id();
19712 var iframe = this.owner.wrap.createChild({
19714 cls: 'form-control', // bootstrap..
19716 name: this.frameId,
19717 frameBorder : 'no',
19718 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19723 this.iframe = iframe.dom;
19725 this.assignDocWin();
19727 this.doc.designMode = 'on';
19730 this.doc.write(this.getDocMarkup());
19734 var task = { // must defer to wait for browser to be ready
19736 //console.log("run task?" + this.doc.readyState);
19737 this.assignDocWin();
19738 if(this.doc.body || this.doc.readyState == 'complete'){
19740 this.doc.designMode="on";
19744 Roo.TaskMgr.stop(task);
19745 this.initEditor.defer(10, this);
19752 Roo.TaskMgr.start(task);
19757 onResize : function(w, h)
19759 Roo.log('resize: ' +w + ',' + h );
19760 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19764 if(typeof w == 'number'){
19766 this.iframe.style.width = w + 'px';
19768 if(typeof h == 'number'){
19770 this.iframe.style.height = h + 'px';
19772 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19779 * Toggles the editor between standard and source edit mode.
19780 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19782 toggleSourceEdit : function(sourceEditMode){
19784 this.sourceEditMode = sourceEditMode === true;
19786 if(this.sourceEditMode){
19788 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19791 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19792 //this.iframe.className = '';
19795 //this.setSize(this.owner.wrap.getSize());
19796 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19803 * Protected method that will not generally be called directly. If you need/want
19804 * custom HTML cleanup, this is the method you should override.
19805 * @param {String} html The HTML to be cleaned
19806 * return {String} The cleaned HTML
19808 cleanHtml : function(html){
19809 html = String(html);
19810 if(html.length > 5){
19811 if(Roo.isSafari){ // strip safari nonsense
19812 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19815 if(html == ' '){
19822 * HTML Editor -> Textarea
19823 * Protected method that will not generally be called directly. Syncs the contents
19824 * of the editor iframe with the textarea.
19826 syncValue : function(){
19827 if(this.initialized){
19828 var bd = (this.doc.body || this.doc.documentElement);
19829 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19830 var html = bd.innerHTML;
19832 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19833 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19835 html = '<div style="'+m[0]+'">' + html + '</div>';
19838 html = this.cleanHtml(html);
19839 // fix up the special chars.. normaly like back quotes in word...
19840 // however we do not want to do this with chinese..
19841 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19842 var cc = b.charCodeAt();
19844 (cc >= 0x4E00 && cc < 0xA000 ) ||
19845 (cc >= 0x3400 && cc < 0x4E00 ) ||
19846 (cc >= 0xf900 && cc < 0xfb00 )
19852 if(this.owner.fireEvent('beforesync', this, html) !== false){
19853 this.el.dom.value = html;
19854 this.owner.fireEvent('sync', this, html);
19860 * Protected method that will not generally be called directly. Pushes the value of the textarea
19861 * into the iframe editor.
19863 pushValue : function(){
19864 if(this.initialized){
19865 var v = this.el.dom.value.trim();
19867 // if(v.length < 1){
19871 if(this.owner.fireEvent('beforepush', this, v) !== false){
19872 var d = (this.doc.body || this.doc.documentElement);
19874 this.cleanUpPaste();
19875 this.el.dom.value = d.innerHTML;
19876 this.owner.fireEvent('push', this, v);
19882 deferFocus : function(){
19883 this.focus.defer(10, this);
19887 focus : function(){
19888 if(this.win && !this.sourceEditMode){
19895 assignDocWin: function()
19897 var iframe = this.iframe;
19900 this.doc = iframe.contentWindow.document;
19901 this.win = iframe.contentWindow;
19903 // if (!Roo.get(this.frameId)) {
19906 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19907 // this.win = Roo.get(this.frameId).dom.contentWindow;
19909 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19913 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19914 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19919 initEditor : function(){
19920 //console.log("INIT EDITOR");
19921 this.assignDocWin();
19925 this.doc.designMode="on";
19927 this.doc.write(this.getDocMarkup());
19930 var dbody = (this.doc.body || this.doc.documentElement);
19931 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19932 // this copies styles from the containing element into thsi one..
19933 // not sure why we need all of this..
19934 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19936 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19937 //ss['background-attachment'] = 'fixed'; // w3c
19938 dbody.bgProperties = 'fixed'; // ie
19939 //Roo.DomHelper.applyStyles(dbody, ss);
19940 Roo.EventManager.on(this.doc, {
19941 //'mousedown': this.onEditorEvent,
19942 'mouseup': this.onEditorEvent,
19943 'dblclick': this.onEditorEvent,
19944 'click': this.onEditorEvent,
19945 'keyup': this.onEditorEvent,
19950 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19952 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19953 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19955 this.initialized = true;
19957 this.owner.fireEvent('initialize', this);
19962 onDestroy : function(){
19968 //for (var i =0; i < this.toolbars.length;i++) {
19969 // // fixme - ask toolbars for heights?
19970 // this.toolbars[i].onDestroy();
19973 //this.wrap.dom.innerHTML = '';
19974 //this.wrap.remove();
19979 onFirstFocus : function(){
19981 this.assignDocWin();
19984 this.activated = true;
19987 if(Roo.isGecko){ // prevent silly gecko errors
19989 var s = this.win.getSelection();
19990 if(!s.focusNode || s.focusNode.nodeType != 3){
19991 var r = s.getRangeAt(0);
19992 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19997 this.execCmd('useCSS', true);
19998 this.execCmd('styleWithCSS', false);
20001 this.owner.fireEvent('activate', this);
20005 adjustFont: function(btn){
20006 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20007 //if(Roo.isSafari){ // safari
20010 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20011 if(Roo.isSafari){ // safari
20012 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20013 v = (v < 10) ? 10 : v;
20014 v = (v > 48) ? 48 : v;
20015 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20020 v = Math.max(1, v+adjust);
20022 this.execCmd('FontSize', v );
20025 onEditorEvent : function(e)
20027 this.owner.fireEvent('editorevent', this, e);
20028 // this.updateToolbar();
20029 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20032 insertTag : function(tg)
20034 // could be a bit smarter... -> wrap the current selected tRoo..
20035 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20037 range = this.createRange(this.getSelection());
20038 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20039 wrappingNode.appendChild(range.extractContents());
20040 range.insertNode(wrappingNode);
20047 this.execCmd("formatblock", tg);
20051 insertText : function(txt)
20055 var range = this.createRange();
20056 range.deleteContents();
20057 //alert(Sender.getAttribute('label'));
20059 range.insertNode(this.doc.createTextNode(txt));
20065 * Executes a Midas editor command on the editor document and performs necessary focus and
20066 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20067 * @param {String} cmd The Midas command
20068 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20070 relayCmd : function(cmd, value){
20072 this.execCmd(cmd, value);
20073 this.owner.fireEvent('editorevent', this);
20074 //this.updateToolbar();
20075 this.owner.deferFocus();
20079 * Executes a Midas editor command directly on the editor document.
20080 * For visual commands, you should use {@link #relayCmd} instead.
20081 * <b>This should only be called after the editor is initialized.</b>
20082 * @param {String} cmd The Midas command
20083 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20085 execCmd : function(cmd, value){
20086 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20093 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20095 * @param {String} text | dom node..
20097 insertAtCursor : function(text)
20102 if(!this.activated){
20108 var r = this.doc.selection.createRange();
20119 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20123 // from jquery ui (MIT licenced)
20125 var win = this.win;
20127 if (win.getSelection && win.getSelection().getRangeAt) {
20128 range = win.getSelection().getRangeAt(0);
20129 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20130 range.insertNode(node);
20131 } else if (win.document.selection && win.document.selection.createRange) {
20132 // no firefox support
20133 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20134 win.document.selection.createRange().pasteHTML(txt);
20136 // no firefox support
20137 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20138 this.execCmd('InsertHTML', txt);
20147 mozKeyPress : function(e){
20149 var c = e.getCharCode(), cmd;
20152 c = String.fromCharCode(c).toLowerCase();
20166 this.cleanUpPaste.defer(100, this);
20174 e.preventDefault();
20182 fixKeys : function(){ // load time branching for fastest keydown performance
20184 return function(e){
20185 var k = e.getKey(), r;
20188 r = this.doc.selection.createRange();
20191 r.pasteHTML('    ');
20198 r = this.doc.selection.createRange();
20200 var target = r.parentElement();
20201 if(!target || target.tagName.toLowerCase() != 'li'){
20203 r.pasteHTML('<br />');
20209 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20210 this.cleanUpPaste.defer(100, this);
20216 }else if(Roo.isOpera){
20217 return function(e){
20218 var k = e.getKey();
20222 this.execCmd('InsertHTML','    ');
20225 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20226 this.cleanUpPaste.defer(100, this);
20231 }else if(Roo.isSafari){
20232 return function(e){
20233 var k = e.getKey();
20237 this.execCmd('InsertText','\t');
20241 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20242 this.cleanUpPaste.defer(100, this);
20250 getAllAncestors: function()
20252 var p = this.getSelectedNode();
20255 a.push(p); // push blank onto stack..
20256 p = this.getParentElement();
20260 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20264 a.push(this.doc.body);
20268 lastSelNode : false,
20271 getSelection : function()
20273 this.assignDocWin();
20274 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20277 getSelectedNode: function()
20279 // this may only work on Gecko!!!
20281 // should we cache this!!!!
20286 var range = this.createRange(this.getSelection()).cloneRange();
20289 var parent = range.parentElement();
20291 var testRange = range.duplicate();
20292 testRange.moveToElementText(parent);
20293 if (testRange.inRange(range)) {
20296 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20299 parent = parent.parentElement;
20304 // is ancestor a text element.
20305 var ac = range.commonAncestorContainer;
20306 if (ac.nodeType == 3) {
20307 ac = ac.parentNode;
20310 var ar = ac.childNodes;
20313 var other_nodes = [];
20314 var has_other_nodes = false;
20315 for (var i=0;i<ar.length;i++) {
20316 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20319 // fullly contained node.
20321 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20326 // probably selected..
20327 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20328 other_nodes.push(ar[i]);
20332 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20337 has_other_nodes = true;
20339 if (!nodes.length && other_nodes.length) {
20340 nodes= other_nodes;
20342 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20348 createRange: function(sel)
20350 // this has strange effects when using with
20351 // top toolbar - not sure if it's a great idea.
20352 //this.editor.contentWindow.focus();
20353 if (typeof sel != "undefined") {
20355 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20357 return this.doc.createRange();
20360 return this.doc.createRange();
20363 getParentElement: function()
20366 this.assignDocWin();
20367 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20369 var range = this.createRange(sel);
20372 var p = range.commonAncestorContainer;
20373 while (p.nodeType == 3) { // text node
20384 * Range intersection.. the hard stuff...
20388 * [ -- selected range --- ]
20392 * if end is before start or hits it. fail.
20393 * if start is after end or hits it fail.
20395 * if either hits (but other is outside. - then it's not
20401 // @see http://www.thismuchiknow.co.uk/?p=64.
20402 rangeIntersectsNode : function(range, node)
20404 var nodeRange = node.ownerDocument.createRange();
20406 nodeRange.selectNode(node);
20408 nodeRange.selectNodeContents(node);
20411 var rangeStartRange = range.cloneRange();
20412 rangeStartRange.collapse(true);
20414 var rangeEndRange = range.cloneRange();
20415 rangeEndRange.collapse(false);
20417 var nodeStartRange = nodeRange.cloneRange();
20418 nodeStartRange.collapse(true);
20420 var nodeEndRange = nodeRange.cloneRange();
20421 nodeEndRange.collapse(false);
20423 return rangeStartRange.compareBoundaryPoints(
20424 Range.START_TO_START, nodeEndRange) == -1 &&
20425 rangeEndRange.compareBoundaryPoints(
20426 Range.START_TO_START, nodeStartRange) == 1;
20430 rangeCompareNode : function(range, node)
20432 var nodeRange = node.ownerDocument.createRange();
20434 nodeRange.selectNode(node);
20436 nodeRange.selectNodeContents(node);
20440 range.collapse(true);
20442 nodeRange.collapse(true);
20444 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20445 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20447 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20449 var nodeIsBefore = ss == 1;
20450 var nodeIsAfter = ee == -1;
20452 if (nodeIsBefore && nodeIsAfter) {
20455 if (!nodeIsBefore && nodeIsAfter) {
20456 return 1; //right trailed.
20459 if (nodeIsBefore && !nodeIsAfter) {
20460 return 2; // left trailed.
20466 // private? - in a new class?
20467 cleanUpPaste : function()
20469 // cleans up the whole document..
20470 Roo.log('cleanuppaste');
20472 this.cleanUpChildren(this.doc.body);
20473 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20474 if (clean != this.doc.body.innerHTML) {
20475 this.doc.body.innerHTML = clean;
20480 cleanWordChars : function(input) {// change the chars to hex code
20481 var he = Roo.HtmlEditorCore;
20483 var output = input;
20484 Roo.each(he.swapCodes, function(sw) {
20485 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20487 output = output.replace(swapper, sw[1]);
20494 cleanUpChildren : function (n)
20496 if (!n.childNodes.length) {
20499 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20500 this.cleanUpChild(n.childNodes[i]);
20507 cleanUpChild : function (node)
20510 //console.log(node);
20511 if (node.nodeName == "#text") {
20512 // clean up silly Windows -- stuff?
20515 if (node.nodeName == "#comment") {
20516 node.parentNode.removeChild(node);
20517 // clean up silly Windows -- stuff?
20520 var lcname = node.tagName.toLowerCase();
20521 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20522 // whitelist of tags..
20524 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20526 node.parentNode.removeChild(node);
20531 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20533 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20534 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20536 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20537 // remove_keep_children = true;
20540 if (remove_keep_children) {
20541 this.cleanUpChildren(node);
20542 // inserts everything just before this node...
20543 while (node.childNodes.length) {
20544 var cn = node.childNodes[0];
20545 node.removeChild(cn);
20546 node.parentNode.insertBefore(cn, node);
20548 node.parentNode.removeChild(node);
20552 if (!node.attributes || !node.attributes.length) {
20553 this.cleanUpChildren(node);
20557 function cleanAttr(n,v)
20560 if (v.match(/^\./) || v.match(/^\//)) {
20563 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20566 if (v.match(/^#/)) {
20569 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20570 node.removeAttribute(n);
20574 var cwhite = this.cwhite;
20575 var cblack = this.cblack;
20577 function cleanStyle(n,v)
20579 if (v.match(/expression/)) { //XSS?? should we even bother..
20580 node.removeAttribute(n);
20584 var parts = v.split(/;/);
20587 Roo.each(parts, function(p) {
20588 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20592 var l = p.split(':').shift().replace(/\s+/g,'');
20593 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20595 if ( cwhite.length && cblack.indexOf(l) > -1) {
20596 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20597 //node.removeAttribute(n);
20601 // only allow 'c whitelisted system attributes'
20602 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20603 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20604 //node.removeAttribute(n);
20614 if (clean.length) {
20615 node.setAttribute(n, clean.join(';'));
20617 node.removeAttribute(n);
20623 for (var i = node.attributes.length-1; i > -1 ; i--) {
20624 var a = node.attributes[i];
20627 if (a.name.toLowerCase().substr(0,2)=='on') {
20628 node.removeAttribute(a.name);
20631 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20632 node.removeAttribute(a.name);
20635 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20636 cleanAttr(a.name,a.value); // fixme..
20639 if (a.name == 'style') {
20640 cleanStyle(a.name,a.value);
20643 /// clean up MS crap..
20644 // tecnically this should be a list of valid class'es..
20647 if (a.name == 'class') {
20648 if (a.value.match(/^Mso/)) {
20649 node.className = '';
20652 if (a.value.match(/body/)) {
20653 node.className = '';
20664 this.cleanUpChildren(node);
20670 * Clean up MS wordisms...
20672 cleanWord : function(node)
20677 this.cleanWord(this.doc.body);
20680 if (node.nodeName == "#text") {
20681 // clean up silly Windows -- stuff?
20684 if (node.nodeName == "#comment") {
20685 node.parentNode.removeChild(node);
20686 // clean up silly Windows -- stuff?
20690 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20691 node.parentNode.removeChild(node);
20695 // remove - but keep children..
20696 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20697 while (node.childNodes.length) {
20698 var cn = node.childNodes[0];
20699 node.removeChild(cn);
20700 node.parentNode.insertBefore(cn, node);
20702 node.parentNode.removeChild(node);
20703 this.iterateChildren(node, this.cleanWord);
20707 if (node.className.length) {
20709 var cn = node.className.split(/\W+/);
20711 Roo.each(cn, function(cls) {
20712 if (cls.match(/Mso[a-zA-Z]+/)) {
20717 node.className = cna.length ? cna.join(' ') : '';
20719 node.removeAttribute("class");
20723 if (node.hasAttribute("lang")) {
20724 node.removeAttribute("lang");
20727 if (node.hasAttribute("style")) {
20729 var styles = node.getAttribute("style").split(";");
20731 Roo.each(styles, function(s) {
20732 if (!s.match(/:/)) {
20735 var kv = s.split(":");
20736 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20739 // what ever is left... we allow.
20742 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20743 if (!nstyle.length) {
20744 node.removeAttribute('style');
20747 this.iterateChildren(node, this.cleanWord);
20753 * iterateChildren of a Node, calling fn each time, using this as the scole..
20754 * @param {DomNode} node node to iterate children of.
20755 * @param {Function} fn method of this class to call on each item.
20757 iterateChildren : function(node, fn)
20759 if (!node.childNodes.length) {
20762 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20763 fn.call(this, node.childNodes[i])
20769 * cleanTableWidths.
20771 * Quite often pasting from word etc.. results in tables with column and widths.
20772 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20775 cleanTableWidths : function(node)
20780 this.cleanTableWidths(this.doc.body);
20785 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20788 Roo.log(node.tagName);
20789 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20790 this.iterateChildren(node, this.cleanTableWidths);
20793 if (node.hasAttribute('width')) {
20794 node.removeAttribute('width');
20798 if (node.hasAttribute("style")) {
20801 var styles = node.getAttribute("style").split(";");
20803 Roo.each(styles, function(s) {
20804 if (!s.match(/:/)) {
20807 var kv = s.split(":");
20808 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20811 // what ever is left... we allow.
20814 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20815 if (!nstyle.length) {
20816 node.removeAttribute('style');
20820 this.iterateChildren(node, this.cleanTableWidths);
20828 domToHTML : function(currentElement, depth, nopadtext) {
20830 depth = depth || 0;
20831 nopadtext = nopadtext || false;
20833 if (!currentElement) {
20834 return this.domToHTML(this.doc.body);
20837 //Roo.log(currentElement);
20839 var allText = false;
20840 var nodeName = currentElement.nodeName;
20841 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20843 if (nodeName == '#text') {
20845 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20850 if (nodeName != 'BODY') {
20853 // Prints the node tagName, such as <A>, <IMG>, etc
20856 for(i = 0; i < currentElement.attributes.length;i++) {
20858 var aname = currentElement.attributes.item(i).name;
20859 if (!currentElement.attributes.item(i).value.length) {
20862 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20865 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20874 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20877 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20882 // Traverse the tree
20884 var currentElementChild = currentElement.childNodes.item(i);
20885 var allText = true;
20886 var innerHTML = '';
20888 while (currentElementChild) {
20889 // Formatting code (indent the tree so it looks nice on the screen)
20890 var nopad = nopadtext;
20891 if (lastnode == 'SPAN') {
20895 if (currentElementChild.nodeName == '#text') {
20896 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20897 toadd = nopadtext ? toadd : toadd.trim();
20898 if (!nopad && toadd.length > 80) {
20899 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20901 innerHTML += toadd;
20904 currentElementChild = currentElement.childNodes.item(i);
20910 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20912 // Recursively traverse the tree structure of the child node
20913 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20914 lastnode = currentElementChild.nodeName;
20916 currentElementChild=currentElement.childNodes.item(i);
20922 // The remaining code is mostly for formatting the tree
20923 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20928 ret+= "</"+tagName+">";
20934 applyBlacklists : function()
20936 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20937 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20941 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20942 if (b.indexOf(tag) > -1) {
20945 this.white.push(tag);
20949 Roo.each(w, function(tag) {
20950 if (b.indexOf(tag) > -1) {
20953 if (this.white.indexOf(tag) > -1) {
20956 this.white.push(tag);
20961 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20962 if (w.indexOf(tag) > -1) {
20965 this.black.push(tag);
20969 Roo.each(b, function(tag) {
20970 if (w.indexOf(tag) > -1) {
20973 if (this.black.indexOf(tag) > -1) {
20976 this.black.push(tag);
20981 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20982 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20986 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20987 if (b.indexOf(tag) > -1) {
20990 this.cwhite.push(tag);
20994 Roo.each(w, function(tag) {
20995 if (b.indexOf(tag) > -1) {
20998 if (this.cwhite.indexOf(tag) > -1) {
21001 this.cwhite.push(tag);
21006 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21007 if (w.indexOf(tag) > -1) {
21010 this.cblack.push(tag);
21014 Roo.each(b, function(tag) {
21015 if (w.indexOf(tag) > -1) {
21018 if (this.cblack.indexOf(tag) > -1) {
21021 this.cblack.push(tag);
21026 setStylesheets : function(stylesheets)
21028 if(typeof(stylesheets) == 'string'){
21029 Roo.get(this.iframe.contentDocument.head).createChild({
21031 rel : 'stylesheet',
21040 Roo.each(stylesheets, function(s) {
21045 Roo.get(_this.iframe.contentDocument.head).createChild({
21047 rel : 'stylesheet',
21056 removeStylesheets : function()
21060 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21065 // hide stuff that is not compatible
21079 * @event specialkey
21083 * @cfg {String} fieldClass @hide
21086 * @cfg {String} focusClass @hide
21089 * @cfg {String} autoCreate @hide
21092 * @cfg {String} inputType @hide
21095 * @cfg {String} invalidClass @hide
21098 * @cfg {String} invalidText @hide
21101 * @cfg {String} msgFx @hide
21104 * @cfg {String} validateOnBlur @hide
21108 Roo.HtmlEditorCore.white = [
21109 'area', 'br', 'img', 'input', 'hr', 'wbr',
21111 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21112 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21113 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21114 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21115 'table', 'ul', 'xmp',
21117 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21120 'dir', 'menu', 'ol', 'ul', 'dl',
21126 Roo.HtmlEditorCore.black = [
21127 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21129 'base', 'basefont', 'bgsound', 'blink', 'body',
21130 'frame', 'frameset', 'head', 'html', 'ilayer',
21131 'iframe', 'layer', 'link', 'meta', 'object',
21132 'script', 'style' ,'title', 'xml' // clean later..
21134 Roo.HtmlEditorCore.clean = [
21135 'script', 'style', 'title', 'xml'
21137 Roo.HtmlEditorCore.remove = [
21142 Roo.HtmlEditorCore.ablack = [
21146 Roo.HtmlEditorCore.aclean = [
21147 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21151 Roo.HtmlEditorCore.pwhite= [
21152 'http', 'https', 'mailto'
21155 // white listed style attributes.
21156 Roo.HtmlEditorCore.cwhite= [
21157 // 'text-align', /// default is to allow most things..
21163 // black listed style attributes.
21164 Roo.HtmlEditorCore.cblack= [
21165 // 'font-size' -- this can be set by the project
21169 Roo.HtmlEditorCore.swapCodes =[
21188 * @class Roo.bootstrap.HtmlEditor
21189 * @extends Roo.bootstrap.TextArea
21190 * Bootstrap HtmlEditor class
21193 * Create a new HtmlEditor
21194 * @param {Object} config The config object
21197 Roo.bootstrap.HtmlEditor = function(config){
21198 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21199 if (!this.toolbars) {
21200 this.toolbars = [];
21202 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21205 * @event initialize
21206 * Fires when the editor is fully initialized (including the iframe)
21207 * @param {HtmlEditor} this
21212 * Fires when the editor is first receives the focus. Any insertion must wait
21213 * until after this event.
21214 * @param {HtmlEditor} this
21218 * @event beforesync
21219 * Fires before the textarea is updated with content from the editor iframe. Return false
21220 * to cancel the sync.
21221 * @param {HtmlEditor} this
21222 * @param {String} html
21226 * @event beforepush
21227 * Fires before the iframe editor is updated with content from the textarea. Return false
21228 * to cancel the push.
21229 * @param {HtmlEditor} this
21230 * @param {String} html
21235 * Fires when the textarea is updated with content from the editor iframe.
21236 * @param {HtmlEditor} this
21237 * @param {String} html
21242 * Fires when the iframe editor is updated with content from the textarea.
21243 * @param {HtmlEditor} this
21244 * @param {String} html
21248 * @event editmodechange
21249 * Fires when the editor switches edit modes
21250 * @param {HtmlEditor} this
21251 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21253 editmodechange: true,
21255 * @event editorevent
21256 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21257 * @param {HtmlEditor} this
21261 * @event firstfocus
21262 * Fires when on first focus - needed by toolbars..
21263 * @param {HtmlEditor} this
21268 * Auto save the htmlEditor value as a file into Events
21269 * @param {HtmlEditor} this
21273 * @event savedpreview
21274 * preview the saved version of htmlEditor
21275 * @param {HtmlEditor} this
21282 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21286 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21291 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21296 * @cfg {Number} height (in pixels)
21300 * @cfg {Number} width (in pixels)
21305 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21308 stylesheets: false,
21313 // private properties
21314 validationEvent : false,
21316 initialized : false,
21319 onFocus : Roo.emptyFn,
21321 hideMode:'offsets',
21324 tbContainer : false,
21326 toolbarContainer :function() {
21327 return this.wrap.select('.x-html-editor-tb',true).first();
21331 * Protected method that will not generally be called directly. It
21332 * is called when the editor creates its toolbar. Override this method if you need to
21333 * add custom toolbar buttons.
21334 * @param {HtmlEditor} editor
21336 createToolbar : function(){
21338 Roo.log("create toolbars");
21340 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21341 this.toolbars[0].render(this.toolbarContainer());
21345 // if (!editor.toolbars || !editor.toolbars.length) {
21346 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21349 // for (var i =0 ; i < editor.toolbars.length;i++) {
21350 // editor.toolbars[i] = Roo.factory(
21351 // typeof(editor.toolbars[i]) == 'string' ?
21352 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21353 // Roo.bootstrap.HtmlEditor);
21354 // editor.toolbars[i].init(editor);
21360 onRender : function(ct, position)
21362 // Roo.log("Call onRender: " + this.xtype);
21364 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21366 this.wrap = this.inputEl().wrap({
21367 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21370 this.editorcore.onRender(ct, position);
21372 if (this.resizable) {
21373 this.resizeEl = new Roo.Resizable(this.wrap, {
21377 minHeight : this.height,
21378 height: this.height,
21379 handles : this.resizable,
21382 resize : function(r, w, h) {
21383 _t.onResize(w,h); // -something
21389 this.createToolbar(this);
21392 if(!this.width && this.resizable){
21393 this.setSize(this.wrap.getSize());
21395 if (this.resizeEl) {
21396 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21397 // should trigger onReize..
21403 onResize : function(w, h)
21405 Roo.log('resize: ' +w + ',' + h );
21406 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21410 if(this.inputEl() ){
21411 if(typeof w == 'number'){
21412 var aw = w - this.wrap.getFrameWidth('lr');
21413 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21416 if(typeof h == 'number'){
21417 var tbh = -11; // fixme it needs to tool bar size!
21418 for (var i =0; i < this.toolbars.length;i++) {
21419 // fixme - ask toolbars for heights?
21420 tbh += this.toolbars[i].el.getHeight();
21421 //if (this.toolbars[i].footer) {
21422 // tbh += this.toolbars[i].footer.el.getHeight();
21430 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21431 ah -= 5; // knock a few pixes off for look..
21432 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21436 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21437 this.editorcore.onResize(ew,eh);
21442 * Toggles the editor between standard and source edit mode.
21443 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21445 toggleSourceEdit : function(sourceEditMode)
21447 this.editorcore.toggleSourceEdit(sourceEditMode);
21449 if(this.editorcore.sourceEditMode){
21450 Roo.log('editor - showing textarea');
21453 // Roo.log(this.syncValue());
21455 this.inputEl().removeClass(['hide', 'x-hidden']);
21456 this.inputEl().dom.removeAttribute('tabIndex');
21457 this.inputEl().focus();
21459 Roo.log('editor - hiding textarea');
21461 // Roo.log(this.pushValue());
21464 this.inputEl().addClass(['hide', 'x-hidden']);
21465 this.inputEl().dom.setAttribute('tabIndex', -1);
21466 //this.deferFocus();
21469 if(this.resizable){
21470 this.setSize(this.wrap.getSize());
21473 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21476 // private (for BoxComponent)
21477 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21479 // private (for BoxComponent)
21480 getResizeEl : function(){
21484 // private (for BoxComponent)
21485 getPositionEl : function(){
21490 initEvents : function(){
21491 this.originalValue = this.getValue();
21495 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21498 // markInvalid : Roo.emptyFn,
21500 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21503 // clearInvalid : Roo.emptyFn,
21505 setValue : function(v){
21506 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21507 this.editorcore.pushValue();
21512 deferFocus : function(){
21513 this.focus.defer(10, this);
21517 focus : function(){
21518 this.editorcore.focus();
21524 onDestroy : function(){
21530 for (var i =0; i < this.toolbars.length;i++) {
21531 // fixme - ask toolbars for heights?
21532 this.toolbars[i].onDestroy();
21535 this.wrap.dom.innerHTML = '';
21536 this.wrap.remove();
21541 onFirstFocus : function(){
21542 //Roo.log("onFirstFocus");
21543 this.editorcore.onFirstFocus();
21544 for (var i =0; i < this.toolbars.length;i++) {
21545 this.toolbars[i].onFirstFocus();
21551 syncValue : function()
21553 this.editorcore.syncValue();
21556 pushValue : function()
21558 this.editorcore.pushValue();
21562 // hide stuff that is not compatible
21576 * @event specialkey
21580 * @cfg {String} fieldClass @hide
21583 * @cfg {String} focusClass @hide
21586 * @cfg {String} autoCreate @hide
21589 * @cfg {String} inputType @hide
21592 * @cfg {String} invalidClass @hide
21595 * @cfg {String} invalidText @hide
21598 * @cfg {String} msgFx @hide
21601 * @cfg {String} validateOnBlur @hide
21610 Roo.namespace('Roo.bootstrap.htmleditor');
21612 * @class Roo.bootstrap.HtmlEditorToolbar1
21617 new Roo.bootstrap.HtmlEditor({
21620 new Roo.bootstrap.HtmlEditorToolbar1({
21621 disable : { fonts: 1 , format: 1, ..., ... , ...],
21627 * @cfg {Object} disable List of elements to disable..
21628 * @cfg {Array} btns List of additional buttons.
21632 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21635 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21638 Roo.apply(this, config);
21640 // default disabled, based on 'good practice'..
21641 this.disable = this.disable || {};
21642 Roo.applyIf(this.disable, {
21645 specialElements : true
21647 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21649 this.editor = config.editor;
21650 this.editorcore = config.editor.editorcore;
21652 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21654 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21655 // dont call parent... till later.
21657 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21662 editorcore : false,
21667 "h1","h2","h3","h4","h5","h6",
21669 "abbr", "acronym", "address", "cite", "samp", "var",
21673 onRender : function(ct, position)
21675 // Roo.log("Call onRender: " + this.xtype);
21677 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21679 this.el.dom.style.marginBottom = '0';
21681 var editorcore = this.editorcore;
21682 var editor= this.editor;
21685 var btn = function(id,cmd , toggle, handler){
21687 var event = toggle ? 'toggle' : 'click';
21692 xns: Roo.bootstrap,
21695 enableToggle:toggle !== false,
21697 pressed : toggle ? false : null,
21700 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21701 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21710 xns: Roo.bootstrap,
21711 glyphicon : 'font',
21715 xns: Roo.bootstrap,
21719 Roo.each(this.formats, function(f) {
21720 style.menu.items.push({
21722 xns: Roo.bootstrap,
21723 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21728 editorcore.insertTag(this.tagname);
21735 children.push(style);
21738 btn('bold',false,true);
21739 btn('italic',false,true);
21740 btn('align-left', 'justifyleft',true);
21741 btn('align-center', 'justifycenter',true);
21742 btn('align-right' , 'justifyright',true);
21743 btn('link', false, false, function(btn) {
21744 //Roo.log("create link?");
21745 var url = prompt(this.createLinkText, this.defaultLinkValue);
21746 if(url && url != 'http:/'+'/'){
21747 this.editorcore.relayCmd('createlink', url);
21750 btn('list','insertunorderedlist',true);
21751 btn('pencil', false,true, function(btn){
21754 this.toggleSourceEdit(btn.pressed);
21760 xns: Roo.bootstrap,
21765 xns: Roo.bootstrap,
21770 cog.menu.items.push({
21772 xns: Roo.bootstrap,
21773 html : Clean styles,
21778 editorcore.insertTag(this.tagname);
21787 this.xtype = 'NavSimplebar';
21789 for(var i=0;i< children.length;i++) {
21791 this.buttons.add(this.addxtypeChild(children[i]));
21795 editor.on('editorevent', this.updateToolbar, this);
21797 onBtnClick : function(id)
21799 this.editorcore.relayCmd(id);
21800 this.editorcore.focus();
21804 * Protected method that will not generally be called directly. It triggers
21805 * a toolbar update by reading the markup state of the current selection in the editor.
21807 updateToolbar: function(){
21809 if(!this.editorcore.activated){
21810 this.editor.onFirstFocus(); // is this neeed?
21814 var btns = this.buttons;
21815 var doc = this.editorcore.doc;
21816 btns.get('bold').setActive(doc.queryCommandState('bold'));
21817 btns.get('italic').setActive(doc.queryCommandState('italic'));
21818 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21820 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21821 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21822 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21824 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21825 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21828 var ans = this.editorcore.getAllAncestors();
21829 if (this.formatCombo) {
21832 var store = this.formatCombo.store;
21833 this.formatCombo.setValue("");
21834 for (var i =0; i < ans.length;i++) {
21835 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21837 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21845 // hides menus... - so this cant be on a menu...
21846 Roo.bootstrap.MenuMgr.hideAll();
21848 Roo.bootstrap.MenuMgr.hideAll();
21849 //this.editorsyncValue();
21851 onFirstFocus: function() {
21852 this.buttons.each(function(item){
21856 toggleSourceEdit : function(sourceEditMode){
21859 if(sourceEditMode){
21860 Roo.log("disabling buttons");
21861 this.buttons.each( function(item){
21862 if(item.cmd != 'pencil'){
21868 Roo.log("enabling buttons");
21869 if(this.editorcore.initialized){
21870 this.buttons.each( function(item){
21876 Roo.log("calling toggole on editor");
21877 // tell the editor that it's been pressed..
21878 this.editor.toggleSourceEdit(sourceEditMode);
21888 * @class Roo.bootstrap.Table.AbstractSelectionModel
21889 * @extends Roo.util.Observable
21890 * Abstract base class for grid SelectionModels. It provides the interface that should be
21891 * implemented by descendant classes. This class should not be directly instantiated.
21894 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21895 this.locked = false;
21896 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21900 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21901 /** @ignore Called by the grid automatically. Do not call directly. */
21902 init : function(grid){
21908 * Locks the selections.
21911 this.locked = true;
21915 * Unlocks the selections.
21917 unlock : function(){
21918 this.locked = false;
21922 * Returns true if the selections are locked.
21923 * @return {Boolean}
21925 isLocked : function(){
21926 return this.locked;
21930 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21931 * @class Roo.bootstrap.Table.RowSelectionModel
21932 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21933 * It supports multiple selections and keyboard selection/navigation.
21935 * @param {Object} config
21938 Roo.bootstrap.Table.RowSelectionModel = function(config){
21939 Roo.apply(this, config);
21940 this.selections = new Roo.util.MixedCollection(false, function(o){
21945 this.lastActive = false;
21949 * @event selectionchange
21950 * Fires when the selection changes
21951 * @param {SelectionModel} this
21953 "selectionchange" : true,
21955 * @event afterselectionchange
21956 * Fires after the selection changes (eg. by key press or clicking)
21957 * @param {SelectionModel} this
21959 "afterselectionchange" : true,
21961 * @event beforerowselect
21962 * Fires when a row is selected being selected, return false to cancel.
21963 * @param {SelectionModel} this
21964 * @param {Number} rowIndex The selected index
21965 * @param {Boolean} keepExisting False if other selections will be cleared
21967 "beforerowselect" : true,
21970 * Fires when a row is selected.
21971 * @param {SelectionModel} this
21972 * @param {Number} rowIndex The selected index
21973 * @param {Roo.data.Record} r The record
21975 "rowselect" : true,
21977 * @event rowdeselect
21978 * Fires when a row is deselected.
21979 * @param {SelectionModel} this
21980 * @param {Number} rowIndex The selected index
21982 "rowdeselect" : true
21984 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21985 this.locked = false;
21988 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21990 * @cfg {Boolean} singleSelect
21991 * True to allow selection of only one row at a time (defaults to false)
21993 singleSelect : false,
21996 initEvents : function(){
21998 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21999 this.grid.on("mousedown", this.handleMouseDown, this);
22000 }else{ // allow click to work like normal
22001 this.grid.on("rowclick", this.handleDragableRowClick, this);
22004 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22005 "up" : function(e){
22007 this.selectPrevious(e.shiftKey);
22008 }else if(this.last !== false && this.lastActive !== false){
22009 var last = this.last;
22010 this.selectRange(this.last, this.lastActive-1);
22011 this.grid.getView().focusRow(this.lastActive);
22012 if(last !== false){
22016 this.selectFirstRow();
22018 this.fireEvent("afterselectionchange", this);
22020 "down" : function(e){
22022 this.selectNext(e.shiftKey);
22023 }else if(this.last !== false && this.lastActive !== false){
22024 var last = this.last;
22025 this.selectRange(this.last, this.lastActive+1);
22026 this.grid.getView().focusRow(this.lastActive);
22027 if(last !== false){
22031 this.selectFirstRow();
22033 this.fireEvent("afterselectionchange", this);
22038 var view = this.grid.view;
22039 view.on("refresh", this.onRefresh, this);
22040 view.on("rowupdated", this.onRowUpdated, this);
22041 view.on("rowremoved", this.onRemove, this);
22045 onRefresh : function(){
22046 var ds = this.grid.dataSource, i, v = this.grid.view;
22047 var s = this.selections;
22048 s.each(function(r){
22049 if((i = ds.indexOfId(r.id)) != -1){
22058 onRemove : function(v, index, r){
22059 this.selections.remove(r);
22063 onRowUpdated : function(v, index, r){
22064 if(this.isSelected(r)){
22065 v.onRowSelect(index);
22071 * @param {Array} records The records to select
22072 * @param {Boolean} keepExisting (optional) True to keep existing selections
22074 selectRecords : function(records, keepExisting){
22076 this.clearSelections();
22078 var ds = this.grid.dataSource;
22079 for(var i = 0, len = records.length; i < len; i++){
22080 this.selectRow(ds.indexOf(records[i]), true);
22085 * Gets the number of selected rows.
22088 getCount : function(){
22089 return this.selections.length;
22093 * Selects the first row in the grid.
22095 selectFirstRow : function(){
22100 * Select the last row.
22101 * @param {Boolean} keepExisting (optional) True to keep existing selections
22103 selectLastRow : function(keepExisting){
22104 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22108 * Selects the row immediately following the last selected row.
22109 * @param {Boolean} keepExisting (optional) True to keep existing selections
22111 selectNext : function(keepExisting){
22112 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22113 this.selectRow(this.last+1, keepExisting);
22114 this.grid.getView().focusRow(this.last);
22119 * Selects the row that precedes the last selected row.
22120 * @param {Boolean} keepExisting (optional) True to keep existing selections
22122 selectPrevious : function(keepExisting){
22124 this.selectRow(this.last-1, keepExisting);
22125 this.grid.getView().focusRow(this.last);
22130 * Returns the selected records
22131 * @return {Array} Array of selected records
22133 getSelections : function(){
22134 return [].concat(this.selections.items);
22138 * Returns the first selected record.
22141 getSelected : function(){
22142 return this.selections.itemAt(0);
22147 * Clears all selections.
22149 clearSelections : function(fast){
22154 var ds = this.grid.dataSource;
22155 var s = this.selections;
22156 s.each(function(r){
22157 this.deselectRow(ds.indexOfId(r.id));
22161 this.selections.clear();
22168 * Selects all rows.
22170 selectAll : function(){
22174 this.selections.clear();
22175 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22176 this.selectRow(i, true);
22181 * Returns True if there is a selection.
22182 * @return {Boolean}
22184 hasSelection : function(){
22185 return this.selections.length > 0;
22189 * Returns True if the specified row is selected.
22190 * @param {Number/Record} record The record or index of the record to check
22191 * @return {Boolean}
22193 isSelected : function(index){
22194 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22195 return (r && this.selections.key(r.id) ? true : false);
22199 * Returns True if the specified record id is selected.
22200 * @param {String} id The id of record to check
22201 * @return {Boolean}
22203 isIdSelected : function(id){
22204 return (this.selections.key(id) ? true : false);
22208 handleMouseDown : function(e, t){
22209 var view = this.grid.getView(), rowIndex;
22210 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22213 if(e.shiftKey && this.last !== false){
22214 var last = this.last;
22215 this.selectRange(last, rowIndex, e.ctrlKey);
22216 this.last = last; // reset the last
22217 view.focusRow(rowIndex);
22219 var isSelected = this.isSelected(rowIndex);
22220 if(e.button !== 0 && isSelected){
22221 view.focusRow(rowIndex);
22222 }else if(e.ctrlKey && isSelected){
22223 this.deselectRow(rowIndex);
22224 }else if(!isSelected){
22225 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22226 view.focusRow(rowIndex);
22229 this.fireEvent("afterselectionchange", this);
22232 handleDragableRowClick : function(grid, rowIndex, e)
22234 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22235 this.selectRow(rowIndex, false);
22236 grid.view.focusRow(rowIndex);
22237 this.fireEvent("afterselectionchange", this);
22242 * Selects multiple rows.
22243 * @param {Array} rows Array of the indexes of the row to select
22244 * @param {Boolean} keepExisting (optional) True to keep existing selections
22246 selectRows : function(rows, keepExisting){
22248 this.clearSelections();
22250 for(var i = 0, len = rows.length; i < len; i++){
22251 this.selectRow(rows[i], true);
22256 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22257 * @param {Number} startRow The index of the first row in the range
22258 * @param {Number} endRow The index of the last row in the range
22259 * @param {Boolean} keepExisting (optional) True to retain existing selections
22261 selectRange : function(startRow, endRow, keepExisting){
22266 this.clearSelections();
22268 if(startRow <= endRow){
22269 for(var i = startRow; i <= endRow; i++){
22270 this.selectRow(i, true);
22273 for(var i = startRow; i >= endRow; i--){
22274 this.selectRow(i, true);
22280 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22281 * @param {Number} startRow The index of the first row in the range
22282 * @param {Number} endRow The index of the last row in the range
22284 deselectRange : function(startRow, endRow, preventViewNotify){
22288 for(var i = startRow; i <= endRow; i++){
22289 this.deselectRow(i, preventViewNotify);
22295 * @param {Number} row The index of the row to select
22296 * @param {Boolean} keepExisting (optional) True to keep existing selections
22298 selectRow : function(index, keepExisting, preventViewNotify){
22299 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22302 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22303 if(!keepExisting || this.singleSelect){
22304 this.clearSelections();
22306 var r = this.grid.dataSource.getAt(index);
22307 this.selections.add(r);
22308 this.last = this.lastActive = index;
22309 if(!preventViewNotify){
22310 this.grid.getView().onRowSelect(index);
22312 this.fireEvent("rowselect", this, index, r);
22313 this.fireEvent("selectionchange", this);
22319 * @param {Number} row The index of the row to deselect
22321 deselectRow : function(index, preventViewNotify){
22325 if(this.last == index){
22328 if(this.lastActive == index){
22329 this.lastActive = false;
22331 var r = this.grid.dataSource.getAt(index);
22332 this.selections.remove(r);
22333 if(!preventViewNotify){
22334 this.grid.getView().onRowDeselect(index);
22336 this.fireEvent("rowdeselect", this, index);
22337 this.fireEvent("selectionchange", this);
22341 restoreLast : function(){
22343 this.last = this._last;
22348 acceptsNav : function(row, col, cm){
22349 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22353 onEditorKey : function(field, e){
22354 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22359 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22361 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22363 }else if(k == e.ENTER && !e.ctrlKey){
22367 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22369 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22371 }else if(k == e.ESC){
22375 g.startEditing(newCell[0], newCell[1]);
22380 * Ext JS Library 1.1.1
22381 * Copyright(c) 2006-2007, Ext JS, LLC.
22383 * Originally Released Under LGPL - original licence link has changed is not relivant.
22386 * <script type="text/javascript">
22390 * @class Roo.bootstrap.PagingToolbar
22391 * @extends Roo.bootstrap.NavSimplebar
22392 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22394 * Create a new PagingToolbar
22395 * @param {Object} config The config object
22396 * @param {Roo.data.Store} store
22398 Roo.bootstrap.PagingToolbar = function(config)
22400 // old args format still supported... - xtype is prefered..
22401 // created from xtype...
22403 this.ds = config.dataSource;
22405 if (config.store && !this.ds) {
22406 this.store= Roo.factory(config.store, Roo.data);
22407 this.ds = this.store;
22408 this.ds.xmodule = this.xmodule || false;
22411 this.toolbarItems = [];
22412 if (config.items) {
22413 this.toolbarItems = config.items;
22416 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22421 this.bind(this.ds);
22424 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22428 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22430 * @cfg {Roo.data.Store} dataSource
22431 * The underlying data store providing the paged data
22434 * @cfg {String/HTMLElement/Element} container
22435 * container The id or element that will contain the toolbar
22438 * @cfg {Boolean} displayInfo
22439 * True to display the displayMsg (defaults to false)
22442 * @cfg {Number} pageSize
22443 * The number of records to display per page (defaults to 20)
22447 * @cfg {String} displayMsg
22448 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22450 displayMsg : 'Displaying {0} - {1} of {2}',
22452 * @cfg {String} emptyMsg
22453 * The message to display when no records are found (defaults to "No data to display")
22455 emptyMsg : 'No data to display',
22457 * Customizable piece of the default paging text (defaults to "Page")
22460 beforePageText : "Page",
22462 * Customizable piece of the default paging text (defaults to "of %0")
22465 afterPageText : "of {0}",
22467 * Customizable piece of the default paging text (defaults to "First Page")
22470 firstText : "First Page",
22472 * Customizable piece of the default paging text (defaults to "Previous Page")
22475 prevText : "Previous Page",
22477 * Customizable piece of the default paging text (defaults to "Next Page")
22480 nextText : "Next Page",
22482 * Customizable piece of the default paging text (defaults to "Last Page")
22485 lastText : "Last Page",
22487 * Customizable piece of the default paging text (defaults to "Refresh")
22490 refreshText : "Refresh",
22494 onRender : function(ct, position)
22496 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22497 this.navgroup.parentId = this.id;
22498 this.navgroup.onRender(this.el, null);
22499 // add the buttons to the navgroup
22501 if(this.displayInfo){
22502 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22503 this.displayEl = this.el.select('.x-paging-info', true).first();
22504 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22505 // this.displayEl = navel.el.select('span',true).first();
22511 Roo.each(_this.buttons, function(e){ // this might need to use render????
22512 Roo.factory(e).onRender(_this.el, null);
22516 Roo.each(_this.toolbarItems, function(e) {
22517 _this.navgroup.addItem(e);
22521 this.first = this.navgroup.addItem({
22522 tooltip: this.firstText,
22524 icon : 'fa fa-backward',
22526 preventDefault: true,
22527 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22530 this.prev = this.navgroup.addItem({
22531 tooltip: this.prevText,
22533 icon : 'fa fa-step-backward',
22535 preventDefault: true,
22536 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22538 //this.addSeparator();
22541 var field = this.navgroup.addItem( {
22543 cls : 'x-paging-position',
22545 html : this.beforePageText +
22546 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22547 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22550 this.field = field.el.select('input', true).first();
22551 this.field.on("keydown", this.onPagingKeydown, this);
22552 this.field.on("focus", function(){this.dom.select();});
22555 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22556 //this.field.setHeight(18);
22557 //this.addSeparator();
22558 this.next = this.navgroup.addItem({
22559 tooltip: this.nextText,
22561 html : ' <i class="fa fa-step-forward">',
22563 preventDefault: true,
22564 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22566 this.last = this.navgroup.addItem({
22567 tooltip: this.lastText,
22568 icon : 'fa fa-forward',
22571 preventDefault: true,
22572 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22574 //this.addSeparator();
22575 this.loading = this.navgroup.addItem({
22576 tooltip: this.refreshText,
22577 icon: 'fa fa-refresh',
22578 preventDefault: true,
22579 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22585 updateInfo : function(){
22586 if(this.displayEl){
22587 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22588 var msg = count == 0 ?
22592 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22594 this.displayEl.update(msg);
22599 onLoad : function(ds, r, o){
22600 this.cursor = o.params ? o.params.start : 0;
22601 var d = this.getPageData(),
22605 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22606 this.field.dom.value = ap;
22607 this.first.setDisabled(ap == 1);
22608 this.prev.setDisabled(ap == 1);
22609 this.next.setDisabled(ap == ps);
22610 this.last.setDisabled(ap == ps);
22611 this.loading.enable();
22616 getPageData : function(){
22617 var total = this.ds.getTotalCount();
22620 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22621 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22626 onLoadError : function(){
22627 this.loading.enable();
22631 onPagingKeydown : function(e){
22632 var k = e.getKey();
22633 var d = this.getPageData();
22635 var v = this.field.dom.value, pageNum;
22636 if(!v || isNaN(pageNum = parseInt(v, 10))){
22637 this.field.dom.value = d.activePage;
22640 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22641 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22644 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))
22646 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22647 this.field.dom.value = pageNum;
22648 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22651 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22653 var v = this.field.dom.value, pageNum;
22654 var increment = (e.shiftKey) ? 10 : 1;
22655 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22658 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22659 this.field.dom.value = d.activePage;
22662 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22664 this.field.dom.value = parseInt(v, 10) + increment;
22665 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22666 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22673 beforeLoad : function(){
22675 this.loading.disable();
22680 onClick : function(which){
22689 ds.load({params:{start: 0, limit: this.pageSize}});
22692 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22695 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22698 var total = ds.getTotalCount();
22699 var extra = total % this.pageSize;
22700 var lastStart = extra ? (total - extra) : total-this.pageSize;
22701 ds.load({params:{start: lastStart, limit: this.pageSize}});
22704 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22710 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22711 * @param {Roo.data.Store} store The data store to unbind
22713 unbind : function(ds){
22714 ds.un("beforeload", this.beforeLoad, this);
22715 ds.un("load", this.onLoad, this);
22716 ds.un("loadexception", this.onLoadError, this);
22717 ds.un("remove", this.updateInfo, this);
22718 ds.un("add", this.updateInfo, this);
22719 this.ds = undefined;
22723 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22724 * @param {Roo.data.Store} store The data store to bind
22726 bind : function(ds){
22727 ds.on("beforeload", this.beforeLoad, this);
22728 ds.on("load", this.onLoad, this);
22729 ds.on("loadexception", this.onLoadError, this);
22730 ds.on("remove", this.updateInfo, this);
22731 ds.on("add", this.updateInfo, this);
22742 * @class Roo.bootstrap.MessageBar
22743 * @extends Roo.bootstrap.Component
22744 * Bootstrap MessageBar class
22745 * @cfg {String} html contents of the MessageBar
22746 * @cfg {String} weight (info | success | warning | danger) default info
22747 * @cfg {String} beforeClass insert the bar before the given class
22748 * @cfg {Boolean} closable (true | false) default false
22749 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22752 * Create a new Element
22753 * @param {Object} config The config object
22756 Roo.bootstrap.MessageBar = function(config){
22757 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22760 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22766 beforeClass: 'bootstrap-sticky-wrap',
22768 getAutoCreate : function(){
22772 cls: 'alert alert-dismissable alert-' + this.weight,
22777 html: this.html || ''
22783 cfg.cls += ' alert-messages-fixed';
22797 onRender : function(ct, position)
22799 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22802 var cfg = Roo.apply({}, this.getAutoCreate());
22806 cfg.cls += ' ' + this.cls;
22809 cfg.style = this.style;
22811 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22813 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22816 this.el.select('>button.close').on('click', this.hide, this);
22822 if (!this.rendered) {
22828 this.fireEvent('show', this);
22834 if (!this.rendered) {
22840 this.fireEvent('hide', this);
22843 update : function()
22845 // var e = this.el.dom.firstChild;
22847 // if(this.closable){
22848 // e = e.nextSibling;
22851 // e.data = this.html || '';
22853 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22869 * @class Roo.bootstrap.Graph
22870 * @extends Roo.bootstrap.Component
22871 * Bootstrap Graph class
22875 @cfg {String} graphtype bar | vbar | pie
22876 @cfg {number} g_x coodinator | centre x (pie)
22877 @cfg {number} g_y coodinator | centre y (pie)
22878 @cfg {number} g_r radius (pie)
22879 @cfg {number} g_height height of the chart (respected by all elements in the set)
22880 @cfg {number} g_width width of the chart (respected by all elements in the set)
22881 @cfg {Object} title The title of the chart
22884 -opts (object) options for the chart
22886 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22887 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22889 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.
22890 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22892 o stretch (boolean)
22894 -opts (object) options for the pie
22897 o startAngle (number)
22898 o endAngle (number)
22902 * Create a new Input
22903 * @param {Object} config The config object
22906 Roo.bootstrap.Graph = function(config){
22907 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22913 * The img click event for the img.
22914 * @param {Roo.EventObject} e
22920 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22931 //g_colors: this.colors,
22938 getAutoCreate : function(){
22949 onRender : function(ct,position){
22952 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22954 if (typeof(Raphael) == 'undefined') {
22955 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22959 this.raphael = Raphael(this.el.dom);
22961 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22962 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22963 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22964 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22966 r.text(160, 10, "Single Series Chart").attr(txtattr);
22967 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22968 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22969 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22971 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22972 r.barchart(330, 10, 300, 220, data1);
22973 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22974 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22977 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22978 // r.barchart(30, 30, 560, 250, xdata, {
22979 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22980 // axis : "0 0 1 1",
22981 // axisxlabels : xdata
22982 // //yvalues : cols,
22985 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22987 // this.load(null,xdata,{
22988 // axis : "0 0 1 1",
22989 // axisxlabels : xdata
22994 load : function(graphtype,xdata,opts)
22996 this.raphael.clear();
22998 graphtype = this.graphtype;
23003 var r = this.raphael,
23004 fin = function () {
23005 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23007 fout = function () {
23008 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23010 pfin = function() {
23011 this.sector.stop();
23012 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23015 this.label[0].stop();
23016 this.label[0].attr({ r: 7.5 });
23017 this.label[1].attr({ "font-weight": 800 });
23020 pfout = function() {
23021 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23024 this.label[0].animate({ r: 5 }, 500, "bounce");
23025 this.label[1].attr({ "font-weight": 400 });
23031 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23034 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23037 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23038 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23040 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23047 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23052 setTitle: function(o)
23057 initEvents: function() {
23060 this.el.on('click', this.onClick, this);
23064 onClick : function(e)
23066 Roo.log('img onclick');
23067 this.fireEvent('click', this, e);
23079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23082 * @class Roo.bootstrap.dash.NumberBox
23083 * @extends Roo.bootstrap.Component
23084 * Bootstrap NumberBox class
23085 * @cfg {String} headline Box headline
23086 * @cfg {String} content Box content
23087 * @cfg {String} icon Box icon
23088 * @cfg {String} footer Footer text
23089 * @cfg {String} fhref Footer href
23092 * Create a new NumberBox
23093 * @param {Object} config The config object
23097 Roo.bootstrap.dash.NumberBox = function(config){
23098 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23102 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23111 getAutoCreate : function(){
23115 cls : 'small-box ',
23123 cls : 'roo-headline',
23124 html : this.headline
23128 cls : 'roo-content',
23129 html : this.content
23143 cls : 'ion ' + this.icon
23152 cls : 'small-box-footer',
23153 href : this.fhref || '#',
23157 cfg.cn.push(footer);
23164 onRender : function(ct,position){
23165 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23172 setHeadline: function (value)
23174 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23177 setFooter: function (value, href)
23179 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23182 this.el.select('a.small-box-footer',true).first().attr('href', href);
23187 setContent: function (value)
23189 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23192 initEvents: function()
23206 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23209 * @class Roo.bootstrap.dash.TabBox
23210 * @extends Roo.bootstrap.Component
23211 * Bootstrap TabBox class
23212 * @cfg {String} title Title of the TabBox
23213 * @cfg {String} icon Icon of the TabBox
23214 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23215 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23218 * Create a new TabBox
23219 * @param {Object} config The config object
23223 Roo.bootstrap.dash.TabBox = function(config){
23224 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23229 * When a pane is added
23230 * @param {Roo.bootstrap.dash.TabPane} pane
23234 * @event activatepane
23235 * When a pane is activated
23236 * @param {Roo.bootstrap.dash.TabPane} pane
23238 "activatepane" : true
23246 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23251 tabScrollable : false,
23253 getChildContainer : function()
23255 return this.el.select('.tab-content', true).first();
23258 getAutoCreate : function(){
23262 cls: 'pull-left header',
23270 cls: 'fa ' + this.icon
23276 cls: 'nav nav-tabs pull-right',
23282 if(this.tabScrollable){
23289 cls: 'nav nav-tabs pull-right',
23300 cls: 'nav-tabs-custom',
23305 cls: 'tab-content no-padding',
23313 initEvents : function()
23315 //Roo.log('add add pane handler');
23316 this.on('addpane', this.onAddPane, this);
23319 * Updates the box title
23320 * @param {String} html to set the title to.
23322 setTitle : function(value)
23324 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23326 onAddPane : function(pane)
23328 this.panes.push(pane);
23329 //Roo.log('addpane');
23331 // tabs are rendere left to right..
23332 if(!this.showtabs){
23336 var ctr = this.el.select('.nav-tabs', true).first();
23339 var existing = ctr.select('.nav-tab',true);
23340 var qty = existing.getCount();;
23343 var tab = ctr.createChild({
23345 cls : 'nav-tab' + (qty ? '' : ' active'),
23353 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23356 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23358 pane.el.addClass('active');
23363 onTabClick : function(ev,un,ob,pane)
23365 //Roo.log('tab - prev default');
23366 ev.preventDefault();
23369 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23370 pane.tab.addClass('active');
23371 //Roo.log(pane.title);
23372 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23373 // technically we should have a deactivate event.. but maybe add later.
23374 // and it should not de-activate the selected tab...
23375 this.fireEvent('activatepane', pane);
23376 pane.el.addClass('active');
23377 pane.fireEvent('activate');
23382 getActivePane : function()
23385 Roo.each(this.panes, function(p) {
23386 if(p.el.hasClass('active')){
23407 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23409 * @class Roo.bootstrap.TabPane
23410 * @extends Roo.bootstrap.Component
23411 * Bootstrap TabPane class
23412 * @cfg {Boolean} active (false | true) Default false
23413 * @cfg {String} title title of panel
23417 * Create a new TabPane
23418 * @param {Object} config The config object
23421 Roo.bootstrap.dash.TabPane = function(config){
23422 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23428 * When a pane is activated
23429 * @param {Roo.bootstrap.dash.TabPane} pane
23436 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23441 // the tabBox that this is attached to.
23444 getAutoCreate : function()
23452 cfg.cls += ' active';
23457 initEvents : function()
23459 //Roo.log('trigger add pane handler');
23460 this.parent().fireEvent('addpane', this)
23464 * Updates the tab title
23465 * @param {String} html to set the title to.
23467 setTitle: function(str)
23473 this.tab.select('a', true).first().dom.innerHTML = str;
23490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23493 * @class Roo.bootstrap.menu.Menu
23494 * @extends Roo.bootstrap.Component
23495 * Bootstrap Menu class - container for Menu
23496 * @cfg {String} html Text of the menu
23497 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23498 * @cfg {String} icon Font awesome icon
23499 * @cfg {String} pos Menu align to (top | bottom) default bottom
23503 * Create a new Menu
23504 * @param {Object} config The config object
23508 Roo.bootstrap.menu.Menu = function(config){
23509 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23513 * @event beforeshow
23514 * Fires before this menu is displayed
23515 * @param {Roo.bootstrap.menu.Menu} this
23519 * @event beforehide
23520 * Fires before this menu is hidden
23521 * @param {Roo.bootstrap.menu.Menu} this
23526 * Fires after this menu is displayed
23527 * @param {Roo.bootstrap.menu.Menu} this
23532 * Fires after this menu is hidden
23533 * @param {Roo.bootstrap.menu.Menu} this
23538 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23539 * @param {Roo.bootstrap.menu.Menu} this
23540 * @param {Roo.EventObject} e
23547 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23551 weight : 'default',
23556 getChildContainer : function() {
23557 if(this.isSubMenu){
23561 return this.el.select('ul.dropdown-menu', true).first();
23564 getAutoCreate : function()
23569 cls : 'roo-menu-text',
23577 cls : 'fa ' + this.icon
23588 cls : 'dropdown-button btn btn-' + this.weight,
23593 cls : 'dropdown-toggle btn btn-' + this.weight,
23603 cls : 'dropdown-menu'
23609 if(this.pos == 'top'){
23610 cfg.cls += ' dropup';
23613 if(this.isSubMenu){
23616 cls : 'dropdown-menu'
23623 onRender : function(ct, position)
23625 this.isSubMenu = ct.hasClass('dropdown-submenu');
23627 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23630 initEvents : function()
23632 if(this.isSubMenu){
23636 this.hidden = true;
23638 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23639 this.triggerEl.on('click', this.onTriggerPress, this);
23641 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23642 this.buttonEl.on('click', this.onClick, this);
23648 if(this.isSubMenu){
23652 return this.el.select('ul.dropdown-menu', true).first();
23655 onClick : function(e)
23657 this.fireEvent("click", this, e);
23660 onTriggerPress : function(e)
23662 if (this.isVisible()) {
23669 isVisible : function(){
23670 return !this.hidden;
23675 this.fireEvent("beforeshow", this);
23677 this.hidden = false;
23678 this.el.addClass('open');
23680 Roo.get(document).on("mouseup", this.onMouseUp, this);
23682 this.fireEvent("show", this);
23689 this.fireEvent("beforehide", this);
23691 this.hidden = true;
23692 this.el.removeClass('open');
23694 Roo.get(document).un("mouseup", this.onMouseUp);
23696 this.fireEvent("hide", this);
23699 onMouseUp : function()
23713 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23716 * @class Roo.bootstrap.menu.Item
23717 * @extends Roo.bootstrap.Component
23718 * Bootstrap MenuItem class
23719 * @cfg {Boolean} submenu (true | false) default false
23720 * @cfg {String} html text of the item
23721 * @cfg {String} href the link
23722 * @cfg {Boolean} disable (true | false) default false
23723 * @cfg {Boolean} preventDefault (true | false) default true
23724 * @cfg {String} icon Font awesome icon
23725 * @cfg {String} pos Submenu align to (left | right) default right
23729 * Create a new Item
23730 * @param {Object} config The config object
23734 Roo.bootstrap.menu.Item = function(config){
23735 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23739 * Fires when the mouse is hovering over this menu
23740 * @param {Roo.bootstrap.menu.Item} this
23741 * @param {Roo.EventObject} e
23746 * Fires when the mouse exits this menu
23747 * @param {Roo.bootstrap.menu.Item} this
23748 * @param {Roo.EventObject} e
23754 * The raw click event for the entire grid.
23755 * @param {Roo.EventObject} e
23761 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23766 preventDefault: true,
23771 getAutoCreate : function()
23776 cls : 'roo-menu-item-text',
23784 cls : 'fa ' + this.icon
23793 href : this.href || '#',
23800 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23804 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23806 if(this.pos == 'left'){
23807 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23814 initEvents : function()
23816 this.el.on('mouseover', this.onMouseOver, this);
23817 this.el.on('mouseout', this.onMouseOut, this);
23819 this.el.select('a', true).first().on('click', this.onClick, this);
23823 onClick : function(e)
23825 if(this.preventDefault){
23826 e.preventDefault();
23829 this.fireEvent("click", this, e);
23832 onMouseOver : function(e)
23834 if(this.submenu && this.pos == 'left'){
23835 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23838 this.fireEvent("mouseover", this, e);
23841 onMouseOut : function(e)
23843 this.fireEvent("mouseout", this, e);
23855 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23858 * @class Roo.bootstrap.menu.Separator
23859 * @extends Roo.bootstrap.Component
23860 * Bootstrap Separator class
23863 * Create a new Separator
23864 * @param {Object} config The config object
23868 Roo.bootstrap.menu.Separator = function(config){
23869 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23872 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23874 getAutoCreate : function(){
23895 * @class Roo.bootstrap.Tooltip
23896 * Bootstrap Tooltip class
23897 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23898 * to determine which dom element triggers the tooltip.
23900 * It needs to add support for additional attributes like tooltip-position
23903 * Create a new Toolti
23904 * @param {Object} config The config object
23907 Roo.bootstrap.Tooltip = function(config){
23908 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23911 Roo.apply(Roo.bootstrap.Tooltip, {
23913 * @function init initialize tooltip monitoring.
23917 currentTip : false,
23918 currentRegion : false,
23924 Roo.get(document).on('mouseover', this.enter ,this);
23925 Roo.get(document).on('mouseout', this.leave, this);
23928 this.currentTip = new Roo.bootstrap.Tooltip();
23931 enter : function(ev)
23933 var dom = ev.getTarget();
23935 //Roo.log(['enter',dom]);
23936 var el = Roo.fly(dom);
23937 if (this.currentEl) {
23939 //Roo.log(this.currentEl);
23940 //Roo.log(this.currentEl.contains(dom));
23941 if (this.currentEl == el) {
23944 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23950 if (this.currentTip.el) {
23951 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23956 // you can not look for children, as if el is the body.. then everythign is the child..
23957 if (!el.attr('tooltip')) { //
23958 if (!el.select("[tooltip]").elements.length) {
23961 // is the mouse over this child...?
23962 bindEl = el.select("[tooltip]").first();
23963 var xy = ev.getXY();
23964 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23965 //Roo.log("not in region.");
23968 //Roo.log("child element over..");
23971 this.currentEl = bindEl;
23972 this.currentTip.bind(bindEl);
23973 this.currentRegion = Roo.lib.Region.getRegion(dom);
23974 this.currentTip.enter();
23977 leave : function(ev)
23979 var dom = ev.getTarget();
23980 //Roo.log(['leave',dom]);
23981 if (!this.currentEl) {
23986 if (dom != this.currentEl.dom) {
23989 var xy = ev.getXY();
23990 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23993 // only activate leave if mouse cursor is outside... bounding box..
23998 if (this.currentTip) {
23999 this.currentTip.leave();
24001 //Roo.log('clear currentEl');
24002 this.currentEl = false;
24007 'left' : ['r-l', [-2,0], 'right'],
24008 'right' : ['l-r', [2,0], 'left'],
24009 'bottom' : ['t-b', [0,2], 'top'],
24010 'top' : [ 'b-t', [0,-2], 'bottom']
24016 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24021 delay : null, // can be { show : 300 , hide: 500}
24025 hoverState : null, //???
24027 placement : 'bottom',
24029 getAutoCreate : function(){
24036 cls : 'tooltip-arrow'
24039 cls : 'tooltip-inner'
24046 bind : function(el)
24052 enter : function () {
24054 if (this.timeout != null) {
24055 clearTimeout(this.timeout);
24058 this.hoverState = 'in';
24059 //Roo.log("enter - show");
24060 if (!this.delay || !this.delay.show) {
24065 this.timeout = setTimeout(function () {
24066 if (_t.hoverState == 'in') {
24069 }, this.delay.show);
24073 clearTimeout(this.timeout);
24075 this.hoverState = 'out';
24076 if (!this.delay || !this.delay.hide) {
24082 this.timeout = setTimeout(function () {
24083 //Roo.log("leave - timeout");
24085 if (_t.hoverState == 'out') {
24087 Roo.bootstrap.Tooltip.currentEl = false;
24095 this.render(document.body);
24098 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24100 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24102 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24104 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24106 var placement = typeof this.placement == 'function' ?
24107 this.placement.call(this, this.el, on_el) :
24110 var autoToken = /\s?auto?\s?/i;
24111 var autoPlace = autoToken.test(placement);
24113 placement = placement.replace(autoToken, '') || 'top';
24117 //this.el.setXY([0,0]);
24119 //this.el.dom.style.display='block';
24121 //this.el.appendTo(on_el);
24123 var p = this.getPosition();
24124 var box = this.el.getBox();
24130 var align = Roo.bootstrap.Tooltip.alignment[placement];
24132 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24134 if(placement == 'top' || placement == 'bottom'){
24136 placement = 'right';
24139 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24140 placement = 'left';
24144 align = Roo.bootstrap.Tooltip.alignment[placement];
24146 this.el.alignTo(this.bindEl, align[0],align[1]);
24147 //var arrow = this.el.select('.arrow',true).first();
24148 //arrow.set(align[2],
24150 this.el.addClass(placement);
24152 this.el.addClass('in fade');
24154 this.hoverState = null;
24156 if (this.el.hasClass('fade')) {
24167 //this.el.setXY([0,0]);
24168 this.el.removeClass('in');
24184 * @class Roo.bootstrap.LocationPicker
24185 * @extends Roo.bootstrap.Component
24186 * Bootstrap LocationPicker class
24187 * @cfg {Number} latitude Position when init default 0
24188 * @cfg {Number} longitude Position when init default 0
24189 * @cfg {Number} zoom default 15
24190 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24191 * @cfg {Boolean} mapTypeControl default false
24192 * @cfg {Boolean} disableDoubleClickZoom default false
24193 * @cfg {Boolean} scrollwheel default true
24194 * @cfg {Boolean} streetViewControl default false
24195 * @cfg {Number} radius default 0
24196 * @cfg {String} locationName
24197 * @cfg {Boolean} draggable default true
24198 * @cfg {Boolean} enableAutocomplete default false
24199 * @cfg {Boolean} enableReverseGeocode default true
24200 * @cfg {String} markerTitle
24203 * Create a new LocationPicker
24204 * @param {Object} config The config object
24208 Roo.bootstrap.LocationPicker = function(config){
24210 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24215 * Fires when the picker initialized.
24216 * @param {Roo.bootstrap.LocationPicker} this
24217 * @param {Google Location} location
24221 * @event positionchanged
24222 * Fires when the picker position changed.
24223 * @param {Roo.bootstrap.LocationPicker} this
24224 * @param {Google Location} location
24226 positionchanged : true,
24229 * Fires when the map resize.
24230 * @param {Roo.bootstrap.LocationPicker} this
24235 * Fires when the map show.
24236 * @param {Roo.bootstrap.LocationPicker} this
24241 * Fires when the map hide.
24242 * @param {Roo.bootstrap.LocationPicker} this
24247 * Fires when click the map.
24248 * @param {Roo.bootstrap.LocationPicker} this
24249 * @param {Map event} e
24253 * @event mapRightClick
24254 * Fires when right click the map.
24255 * @param {Roo.bootstrap.LocationPicker} this
24256 * @param {Map event} e
24258 mapRightClick : true,
24260 * @event markerClick
24261 * Fires when click the marker.
24262 * @param {Roo.bootstrap.LocationPicker} this
24263 * @param {Map event} e
24265 markerClick : true,
24267 * @event markerRightClick
24268 * Fires when right click the marker.
24269 * @param {Roo.bootstrap.LocationPicker} this
24270 * @param {Map event} e
24272 markerRightClick : true,
24274 * @event OverlayViewDraw
24275 * Fires when OverlayView Draw
24276 * @param {Roo.bootstrap.LocationPicker} this
24278 OverlayViewDraw : true,
24280 * @event OverlayViewOnAdd
24281 * Fires when OverlayView Draw
24282 * @param {Roo.bootstrap.LocationPicker} this
24284 OverlayViewOnAdd : true,
24286 * @event OverlayViewOnRemove
24287 * Fires when OverlayView Draw
24288 * @param {Roo.bootstrap.LocationPicker} this
24290 OverlayViewOnRemove : true,
24292 * @event OverlayViewShow
24293 * Fires when OverlayView Draw
24294 * @param {Roo.bootstrap.LocationPicker} this
24295 * @param {Pixel} cpx
24297 OverlayViewShow : true,
24299 * @event OverlayViewHide
24300 * Fires when OverlayView Draw
24301 * @param {Roo.bootstrap.LocationPicker} this
24303 OverlayViewHide : true,
24305 * @event loadexception
24306 * Fires when load google lib failed.
24307 * @param {Roo.bootstrap.LocationPicker} this
24309 loadexception : true
24314 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24316 gMapContext: false,
24322 mapTypeControl: false,
24323 disableDoubleClickZoom: false,
24325 streetViewControl: false,
24329 enableAutocomplete: false,
24330 enableReverseGeocode: true,
24333 getAutoCreate: function()
24338 cls: 'roo-location-picker'
24344 initEvents: function(ct, position)
24346 if(!this.el.getWidth() || this.isApplied()){
24350 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24355 initial: function()
24357 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24358 this.fireEvent('loadexception', this);
24362 if(!this.mapTypeId){
24363 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24366 this.gMapContext = this.GMapContext();
24368 this.initOverlayView();
24370 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24374 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24375 _this.setPosition(_this.gMapContext.marker.position);
24378 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24379 _this.fireEvent('mapClick', this, event);
24383 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24384 _this.fireEvent('mapRightClick', this, event);
24388 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24389 _this.fireEvent('markerClick', this, event);
24393 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24394 _this.fireEvent('markerRightClick', this, event);
24398 this.setPosition(this.gMapContext.location);
24400 this.fireEvent('initial', this, this.gMapContext.location);
24403 initOverlayView: function()
24407 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24411 _this.fireEvent('OverlayViewDraw', _this);
24416 _this.fireEvent('OverlayViewOnAdd', _this);
24419 onRemove: function()
24421 _this.fireEvent('OverlayViewOnRemove', _this);
24424 show: function(cpx)
24426 _this.fireEvent('OverlayViewShow', _this, cpx);
24431 _this.fireEvent('OverlayViewHide', _this);
24437 fromLatLngToContainerPixel: function(event)
24439 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24442 isApplied: function()
24444 return this.getGmapContext() == false ? false : true;
24447 getGmapContext: function()
24449 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24452 GMapContext: function()
24454 var position = new google.maps.LatLng(this.latitude, this.longitude);
24456 var _map = new google.maps.Map(this.el.dom, {
24459 mapTypeId: this.mapTypeId,
24460 mapTypeControl: this.mapTypeControl,
24461 disableDoubleClickZoom: this.disableDoubleClickZoom,
24462 scrollwheel: this.scrollwheel,
24463 streetViewControl: this.streetViewControl,
24464 locationName: this.locationName,
24465 draggable: this.draggable,
24466 enableAutocomplete: this.enableAutocomplete,
24467 enableReverseGeocode: this.enableReverseGeocode
24470 var _marker = new google.maps.Marker({
24471 position: position,
24473 title: this.markerTitle,
24474 draggable: this.draggable
24481 location: position,
24482 radius: this.radius,
24483 locationName: this.locationName,
24484 addressComponents: {
24485 formatted_address: null,
24486 addressLine1: null,
24487 addressLine2: null,
24489 streetNumber: null,
24493 stateOrProvince: null
24496 domContainer: this.el.dom,
24497 geodecoder: new google.maps.Geocoder()
24501 drawCircle: function(center, radius, options)
24503 if (this.gMapContext.circle != null) {
24504 this.gMapContext.circle.setMap(null);
24508 options = Roo.apply({}, options, {
24509 strokeColor: "#0000FF",
24510 strokeOpacity: .35,
24512 fillColor: "#0000FF",
24516 options.map = this.gMapContext.map;
24517 options.radius = radius;
24518 options.center = center;
24519 this.gMapContext.circle = new google.maps.Circle(options);
24520 return this.gMapContext.circle;
24526 setPosition: function(location)
24528 this.gMapContext.location = location;
24529 this.gMapContext.marker.setPosition(location);
24530 this.gMapContext.map.panTo(location);
24531 this.drawCircle(location, this.gMapContext.radius, {});
24535 if (this.gMapContext.settings.enableReverseGeocode) {
24536 this.gMapContext.geodecoder.geocode({
24537 latLng: this.gMapContext.location
24538 }, function(results, status) {
24540 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24541 _this.gMapContext.locationName = results[0].formatted_address;
24542 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24544 _this.fireEvent('positionchanged', this, location);
24551 this.fireEvent('positionchanged', this, location);
24556 google.maps.event.trigger(this.gMapContext.map, "resize");
24558 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24560 this.fireEvent('resize', this);
24563 setPositionByLatLng: function(latitude, longitude)
24565 this.setPosition(new google.maps.LatLng(latitude, longitude));
24568 getCurrentPosition: function()
24571 latitude: this.gMapContext.location.lat(),
24572 longitude: this.gMapContext.location.lng()
24576 getAddressName: function()
24578 return this.gMapContext.locationName;
24581 getAddressComponents: function()
24583 return this.gMapContext.addressComponents;
24586 address_component_from_google_geocode: function(address_components)
24590 for (var i = 0; i < address_components.length; i++) {
24591 var component = address_components[i];
24592 if (component.types.indexOf("postal_code") >= 0) {
24593 result.postalCode = component.short_name;
24594 } else if (component.types.indexOf("street_number") >= 0) {
24595 result.streetNumber = component.short_name;
24596 } else if (component.types.indexOf("route") >= 0) {
24597 result.streetName = component.short_name;
24598 } else if (component.types.indexOf("neighborhood") >= 0) {
24599 result.city = component.short_name;
24600 } else if (component.types.indexOf("locality") >= 0) {
24601 result.city = component.short_name;
24602 } else if (component.types.indexOf("sublocality") >= 0) {
24603 result.district = component.short_name;
24604 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24605 result.stateOrProvince = component.short_name;
24606 } else if (component.types.indexOf("country") >= 0) {
24607 result.country = component.short_name;
24611 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24612 result.addressLine2 = "";
24616 setZoomLevel: function(zoom)
24618 this.gMapContext.map.setZoom(zoom);
24631 this.fireEvent('show', this);
24642 this.fireEvent('hide', this);
24647 Roo.apply(Roo.bootstrap.LocationPicker, {
24649 OverlayView : function(map, options)
24651 options = options || {};
24665 * @class Roo.bootstrap.Alert
24666 * @extends Roo.bootstrap.Component
24667 * Bootstrap Alert class
24668 * @cfg {String} title The title of alert
24669 * @cfg {String} html The content of alert
24670 * @cfg {String} weight ( success | info | warning | danger )
24671 * @cfg {String} faicon font-awesomeicon
24674 * Create a new alert
24675 * @param {Object} config The config object
24679 Roo.bootstrap.Alert = function(config){
24680 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24684 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24691 getAutoCreate : function()
24700 cls : 'roo-alert-icon'
24705 cls : 'roo-alert-title',
24710 cls : 'roo-alert-text',
24717 cfg.cn[0].cls += ' fa ' + this.faicon;
24721 cfg.cls += ' alert-' + this.weight;
24727 initEvents: function()
24729 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24732 setTitle : function(str)
24734 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24737 setText : function(str)
24739 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24742 setWeight : function(weight)
24745 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24748 this.weight = weight;
24750 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24753 setIcon : function(icon)
24756 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24759 this.faicon = icon;
24761 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24782 * @class Roo.bootstrap.UploadCropbox
24783 * @extends Roo.bootstrap.Component
24784 * Bootstrap UploadCropbox class
24785 * @cfg {String} emptyText show when image has been loaded
24786 * @cfg {String} rotateNotify show when image too small to rotate
24787 * @cfg {Number} errorTimeout default 3000
24788 * @cfg {Number} minWidth default 300
24789 * @cfg {Number} minHeight default 300
24790 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24791 * @cfg {Boolean} isDocument (true|false) default false
24792 * @cfg {String} url action url
24793 * @cfg {String} paramName default 'imageUpload'
24794 * @cfg {String} method default POST
24795 * @cfg {Boolean} loadMask (true|false) default true
24796 * @cfg {Boolean} loadingText default 'Loading...'
24799 * Create a new UploadCropbox
24800 * @param {Object} config The config object
24803 Roo.bootstrap.UploadCropbox = function(config){
24804 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24808 * @event beforeselectfile
24809 * Fire before select file
24810 * @param {Roo.bootstrap.UploadCropbox} this
24812 "beforeselectfile" : true,
24815 * Fire after initEvent
24816 * @param {Roo.bootstrap.UploadCropbox} this
24821 * Fire after initEvent
24822 * @param {Roo.bootstrap.UploadCropbox} this
24823 * @param {String} data
24828 * Fire when preparing the file data
24829 * @param {Roo.bootstrap.UploadCropbox} this
24830 * @param {Object} file
24835 * Fire when get exception
24836 * @param {Roo.bootstrap.UploadCropbox} this
24837 * @param {XMLHttpRequest} xhr
24839 "exception" : true,
24841 * @event beforeloadcanvas
24842 * Fire before load the canvas
24843 * @param {Roo.bootstrap.UploadCropbox} this
24844 * @param {String} src
24846 "beforeloadcanvas" : true,
24849 * Fire when trash image
24850 * @param {Roo.bootstrap.UploadCropbox} this
24855 * Fire when download the image
24856 * @param {Roo.bootstrap.UploadCropbox} this
24860 * @event footerbuttonclick
24861 * Fire when footerbuttonclick
24862 * @param {Roo.bootstrap.UploadCropbox} this
24863 * @param {String} type
24865 "footerbuttonclick" : true,
24869 * @param {Roo.bootstrap.UploadCropbox} this
24874 * Fire when rotate the image
24875 * @param {Roo.bootstrap.UploadCropbox} this
24876 * @param {String} pos
24881 * Fire when inspect the file
24882 * @param {Roo.bootstrap.UploadCropbox} this
24883 * @param {Object} file
24888 * Fire when xhr upload the file
24889 * @param {Roo.bootstrap.UploadCropbox} this
24890 * @param {Object} data
24895 * Fire when arrange the file data
24896 * @param {Roo.bootstrap.UploadCropbox} this
24897 * @param {Object} formData
24902 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24905 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24907 emptyText : 'Click to upload image',
24908 rotateNotify : 'Image is too small to rotate',
24909 errorTimeout : 3000,
24923 cropType : 'image/jpeg',
24925 canvasLoaded : false,
24926 isDocument : false,
24928 paramName : 'imageUpload',
24930 loadingText : 'Loading...',
24933 getAutoCreate : function()
24937 cls : 'roo-upload-cropbox',
24941 cls : 'roo-upload-cropbox-selector',
24946 cls : 'roo-upload-cropbox-body',
24947 style : 'cursor:pointer',
24951 cls : 'roo-upload-cropbox-preview'
24955 cls : 'roo-upload-cropbox-thumb'
24959 cls : 'roo-upload-cropbox-empty-notify',
24960 html : this.emptyText
24964 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24965 html : this.rotateNotify
24971 cls : 'roo-upload-cropbox-footer',
24974 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24984 onRender : function(ct, position)
24986 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24988 if (this.buttons.length) {
24990 Roo.each(this.buttons, function(bb) {
24992 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24994 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25000 this.maskEl = this.el;
25004 initEvents : function()
25006 this.urlAPI = (window.createObjectURL && window) ||
25007 (window.URL && URL.revokeObjectURL && URL) ||
25008 (window.webkitURL && webkitURL);
25010 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25011 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25013 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25014 this.selectorEl.hide();
25016 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25017 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25019 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25020 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25021 this.thumbEl.hide();
25023 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25024 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25026 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25027 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25028 this.errorEl.hide();
25030 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25031 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25032 this.footerEl.hide();
25034 this.setThumbBoxSize();
25040 this.fireEvent('initial', this);
25047 window.addEventListener("resize", function() { _this.resize(); } );
25049 this.bodyEl.on('click', this.beforeSelectFile, this);
25052 this.bodyEl.on('touchstart', this.onTouchStart, this);
25053 this.bodyEl.on('touchmove', this.onTouchMove, this);
25054 this.bodyEl.on('touchend', this.onTouchEnd, this);
25058 this.bodyEl.on('mousedown', this.onMouseDown, this);
25059 this.bodyEl.on('mousemove', this.onMouseMove, this);
25060 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25061 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25062 Roo.get(document).on('mouseup', this.onMouseUp, this);
25065 this.selectorEl.on('change', this.onFileSelected, this);
25071 this.baseScale = 1;
25073 this.baseRotate = 1;
25074 this.dragable = false;
25075 this.pinching = false;
25078 this.cropData = false;
25079 this.notifyEl.dom.innerHTML = this.emptyText;
25081 this.selectorEl.dom.value = '';
25085 resize : function()
25087 if(this.fireEvent('resize', this) != false){
25088 this.setThumbBoxPosition();
25089 this.setCanvasPosition();
25093 onFooterButtonClick : function(e, el, o, type)
25096 case 'rotate-left' :
25097 this.onRotateLeft(e);
25099 case 'rotate-right' :
25100 this.onRotateRight(e);
25103 this.beforeSelectFile(e);
25118 this.fireEvent('footerbuttonclick', this, type);
25121 beforeSelectFile : function(e)
25123 e.preventDefault();
25125 if(this.fireEvent('beforeselectfile', this) != false){
25126 this.selectorEl.dom.click();
25130 onFileSelected : function(e)
25132 e.preventDefault();
25134 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25138 var file = this.selectorEl.dom.files[0];
25140 if(this.fireEvent('inspect', this, file) != false){
25141 this.prepare(file);
25146 trash : function(e)
25148 this.fireEvent('trash', this);
25151 download : function(e)
25153 this.fireEvent('download', this);
25156 loadCanvas : function(src)
25158 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25162 this.imageEl = document.createElement('img');
25166 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25168 this.imageEl.src = src;
25172 onLoadCanvas : function()
25174 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25175 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25177 this.bodyEl.un('click', this.beforeSelectFile, this);
25179 this.notifyEl.hide();
25180 this.thumbEl.show();
25181 this.footerEl.show();
25183 this.baseRotateLevel();
25185 if(this.isDocument){
25186 this.setThumbBoxSize();
25189 this.setThumbBoxPosition();
25191 this.baseScaleLevel();
25197 this.canvasLoaded = true;
25200 this.maskEl.unmask();
25205 setCanvasPosition : function()
25207 if(!this.canvasEl){
25211 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25212 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25214 this.previewEl.setLeft(pw);
25215 this.previewEl.setTop(ph);
25219 onMouseDown : function(e)
25223 this.dragable = true;
25224 this.pinching = false;
25226 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25227 this.dragable = false;
25231 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25232 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25236 onMouseMove : function(e)
25240 if(!this.canvasLoaded){
25244 if (!this.dragable){
25248 var minX = Math.ceil(this.thumbEl.getLeft(true));
25249 var minY = Math.ceil(this.thumbEl.getTop(true));
25251 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25252 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25254 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25255 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25257 x = x - this.mouseX;
25258 y = y - this.mouseY;
25260 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25261 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25263 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25264 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25266 this.previewEl.setLeft(bgX);
25267 this.previewEl.setTop(bgY);
25269 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25270 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25273 onMouseUp : function(e)
25277 this.dragable = false;
25280 onMouseWheel : function(e)
25284 this.startScale = this.scale;
25286 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25288 if(!this.zoomable()){
25289 this.scale = this.startScale;
25298 zoomable : function()
25300 var minScale = this.thumbEl.getWidth() / this.minWidth;
25302 if(this.minWidth < this.minHeight){
25303 minScale = this.thumbEl.getHeight() / this.minHeight;
25306 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25307 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25311 (this.rotate == 0 || this.rotate == 180) &&
25313 width > this.imageEl.OriginWidth ||
25314 height > this.imageEl.OriginHeight ||
25315 (width < this.minWidth && height < this.minHeight)
25323 (this.rotate == 90 || this.rotate == 270) &&
25325 width > this.imageEl.OriginWidth ||
25326 height > this.imageEl.OriginHeight ||
25327 (width < this.minHeight && height < this.minWidth)
25334 !this.isDocument &&
25335 (this.rotate == 0 || this.rotate == 180) &&
25337 width < this.minWidth ||
25338 width > this.imageEl.OriginWidth ||
25339 height < this.minHeight ||
25340 height > this.imageEl.OriginHeight
25347 !this.isDocument &&
25348 (this.rotate == 90 || this.rotate == 270) &&
25350 width < this.minHeight ||
25351 width > this.imageEl.OriginWidth ||
25352 height < this.minWidth ||
25353 height > this.imageEl.OriginHeight
25363 onRotateLeft : function(e)
25365 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25367 var minScale = this.thumbEl.getWidth() / this.minWidth;
25369 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25370 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25372 this.startScale = this.scale;
25374 while (this.getScaleLevel() < minScale){
25376 this.scale = this.scale + 1;
25378 if(!this.zoomable()){
25383 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25384 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25389 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25396 this.scale = this.startScale;
25398 this.onRotateFail();
25403 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25405 if(this.isDocument){
25406 this.setThumbBoxSize();
25407 this.setThumbBoxPosition();
25408 this.setCanvasPosition();
25413 this.fireEvent('rotate', this, 'left');
25417 onRotateRight : function(e)
25419 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25421 var minScale = this.thumbEl.getWidth() / this.minWidth;
25423 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25424 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25426 this.startScale = this.scale;
25428 while (this.getScaleLevel() < minScale){
25430 this.scale = this.scale + 1;
25432 if(!this.zoomable()){
25437 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25438 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25443 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25450 this.scale = this.startScale;
25452 this.onRotateFail();
25457 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25459 if(this.isDocument){
25460 this.setThumbBoxSize();
25461 this.setThumbBoxPosition();
25462 this.setCanvasPosition();
25467 this.fireEvent('rotate', this, 'right');
25470 onRotateFail : function()
25472 this.errorEl.show(true);
25476 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25481 this.previewEl.dom.innerHTML = '';
25483 var canvasEl = document.createElement("canvas");
25485 var contextEl = canvasEl.getContext("2d");
25487 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25488 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25489 var center = this.imageEl.OriginWidth / 2;
25491 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25492 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25493 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25494 center = this.imageEl.OriginHeight / 2;
25497 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25499 contextEl.translate(center, center);
25500 contextEl.rotate(this.rotate * Math.PI / 180);
25502 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25504 this.canvasEl = document.createElement("canvas");
25506 this.contextEl = this.canvasEl.getContext("2d");
25508 switch (this.rotate) {
25511 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25512 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25514 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25519 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25520 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25522 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25523 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);
25527 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25532 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25533 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25535 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25536 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);
25540 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);
25545 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25546 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25548 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25549 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25553 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);
25560 this.previewEl.appendChild(this.canvasEl);
25562 this.setCanvasPosition();
25567 if(!this.canvasLoaded){
25571 var imageCanvas = document.createElement("canvas");
25573 var imageContext = imageCanvas.getContext("2d");
25575 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25576 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25578 var center = imageCanvas.width / 2;
25580 imageContext.translate(center, center);
25582 imageContext.rotate(this.rotate * Math.PI / 180);
25584 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25586 var canvas = document.createElement("canvas");
25588 var context = canvas.getContext("2d");
25590 canvas.width = this.minWidth;
25591 canvas.height = this.minHeight;
25593 switch (this.rotate) {
25596 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25597 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25599 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25600 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25602 var targetWidth = this.minWidth - 2 * x;
25603 var targetHeight = this.minHeight - 2 * y;
25607 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25608 scale = targetWidth / width;
25611 if(x > 0 && y == 0){
25612 scale = targetHeight / height;
25615 if(x > 0 && y > 0){
25616 scale = targetWidth / width;
25618 if(width < height){
25619 scale = targetHeight / height;
25623 context.scale(scale, scale);
25625 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25626 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25628 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25629 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25631 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25636 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25637 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25639 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25640 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25642 var targetWidth = this.minWidth - 2 * x;
25643 var targetHeight = this.minHeight - 2 * y;
25647 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25648 scale = targetWidth / width;
25651 if(x > 0 && y == 0){
25652 scale = targetHeight / height;
25655 if(x > 0 && y > 0){
25656 scale = targetWidth / width;
25658 if(width < height){
25659 scale = targetHeight / height;
25663 context.scale(scale, scale);
25665 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25666 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25668 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25669 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25671 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25673 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25678 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25679 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25681 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25682 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25684 var targetWidth = this.minWidth - 2 * x;
25685 var targetHeight = this.minHeight - 2 * y;
25689 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25690 scale = targetWidth / width;
25693 if(x > 0 && y == 0){
25694 scale = targetHeight / height;
25697 if(x > 0 && y > 0){
25698 scale = targetWidth / width;
25700 if(width < height){
25701 scale = targetHeight / height;
25705 context.scale(scale, scale);
25707 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25708 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25710 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25711 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25713 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25714 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25716 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25721 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25722 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25724 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25725 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25727 var targetWidth = this.minWidth - 2 * x;
25728 var targetHeight = this.minHeight - 2 * y;
25732 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25733 scale = targetWidth / width;
25736 if(x > 0 && y == 0){
25737 scale = targetHeight / height;
25740 if(x > 0 && y > 0){
25741 scale = targetWidth / width;
25743 if(width < height){
25744 scale = targetHeight / height;
25748 context.scale(scale, scale);
25750 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25751 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25753 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25754 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25756 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25758 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25765 this.cropData = canvas.toDataURL(this.cropType);
25767 if(this.fireEvent('crop', this, this.cropData) !== false){
25768 this.process(this.file, this.cropData);
25775 setThumbBoxSize : function()
25779 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25780 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25781 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25783 this.minWidth = width;
25784 this.minHeight = height;
25786 if(this.rotate == 90 || this.rotate == 270){
25787 this.minWidth = height;
25788 this.minHeight = width;
25793 width = Math.ceil(this.minWidth * height / this.minHeight);
25795 if(this.minWidth > this.minHeight){
25797 height = Math.ceil(this.minHeight * width / this.minWidth);
25800 this.thumbEl.setStyle({
25801 width : width + 'px',
25802 height : height + 'px'
25809 setThumbBoxPosition : function()
25811 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25812 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25814 this.thumbEl.setLeft(x);
25815 this.thumbEl.setTop(y);
25819 baseRotateLevel : function()
25821 this.baseRotate = 1;
25824 typeof(this.exif) != 'undefined' &&
25825 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25826 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25828 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25831 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25835 baseScaleLevel : function()
25839 if(this.isDocument){
25841 if(this.baseRotate == 6 || this.baseRotate == 8){
25843 height = this.thumbEl.getHeight();
25844 this.baseScale = height / this.imageEl.OriginWidth;
25846 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25847 width = this.thumbEl.getWidth();
25848 this.baseScale = width / this.imageEl.OriginHeight;
25854 height = this.thumbEl.getHeight();
25855 this.baseScale = height / this.imageEl.OriginHeight;
25857 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25858 width = this.thumbEl.getWidth();
25859 this.baseScale = width / this.imageEl.OriginWidth;
25865 if(this.baseRotate == 6 || this.baseRotate == 8){
25867 width = this.thumbEl.getHeight();
25868 this.baseScale = width / this.imageEl.OriginHeight;
25870 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25871 height = this.thumbEl.getWidth();
25872 this.baseScale = height / this.imageEl.OriginHeight;
25875 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25876 height = this.thumbEl.getWidth();
25877 this.baseScale = height / this.imageEl.OriginHeight;
25879 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25880 width = this.thumbEl.getHeight();
25881 this.baseScale = width / this.imageEl.OriginWidth;
25888 width = this.thumbEl.getWidth();
25889 this.baseScale = width / this.imageEl.OriginWidth;
25891 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25892 height = this.thumbEl.getHeight();
25893 this.baseScale = height / this.imageEl.OriginHeight;
25896 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25898 height = this.thumbEl.getHeight();
25899 this.baseScale = height / this.imageEl.OriginHeight;
25901 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25902 width = this.thumbEl.getWidth();
25903 this.baseScale = width / this.imageEl.OriginWidth;
25911 getScaleLevel : function()
25913 return this.baseScale * Math.pow(1.1, this.scale);
25916 onTouchStart : function(e)
25918 if(!this.canvasLoaded){
25919 this.beforeSelectFile(e);
25923 var touches = e.browserEvent.touches;
25929 if(touches.length == 1){
25930 this.onMouseDown(e);
25934 if(touches.length != 2){
25940 for(var i = 0, finger; finger = touches[i]; i++){
25941 coords.push(finger.pageX, finger.pageY);
25944 var x = Math.pow(coords[0] - coords[2], 2);
25945 var y = Math.pow(coords[1] - coords[3], 2);
25947 this.startDistance = Math.sqrt(x + y);
25949 this.startScale = this.scale;
25951 this.pinching = true;
25952 this.dragable = false;
25956 onTouchMove : function(e)
25958 if(!this.pinching && !this.dragable){
25962 var touches = e.browserEvent.touches;
25969 this.onMouseMove(e);
25975 for(var i = 0, finger; finger = touches[i]; i++){
25976 coords.push(finger.pageX, finger.pageY);
25979 var x = Math.pow(coords[0] - coords[2], 2);
25980 var y = Math.pow(coords[1] - coords[3], 2);
25982 this.endDistance = Math.sqrt(x + y);
25984 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25986 if(!this.zoomable()){
25987 this.scale = this.startScale;
25995 onTouchEnd : function(e)
25997 this.pinching = false;
25998 this.dragable = false;
26002 process : function(file, crop)
26005 this.maskEl.mask(this.loadingText);
26008 this.xhr = new XMLHttpRequest();
26010 file.xhr = this.xhr;
26012 this.xhr.open(this.method, this.url, true);
26015 "Accept": "application/json",
26016 "Cache-Control": "no-cache",
26017 "X-Requested-With": "XMLHttpRequest"
26020 for (var headerName in headers) {
26021 var headerValue = headers[headerName];
26023 this.xhr.setRequestHeader(headerName, headerValue);
26029 this.xhr.onload = function()
26031 _this.xhrOnLoad(_this.xhr);
26034 this.xhr.onerror = function()
26036 _this.xhrOnError(_this.xhr);
26039 var formData = new FormData();
26041 formData.append('returnHTML', 'NO');
26044 formData.append('crop', crop);
26047 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26048 formData.append(this.paramName, file, file.name);
26051 if(typeof(file.filename) != 'undefined'){
26052 formData.append('filename', file.filename);
26055 if(typeof(file.mimetype) != 'undefined'){
26056 formData.append('mimetype', file.mimetype);
26059 if(this.fireEvent('arrange', this, formData) != false){
26060 this.xhr.send(formData);
26064 xhrOnLoad : function(xhr)
26067 this.maskEl.unmask();
26070 if (xhr.readyState !== 4) {
26071 this.fireEvent('exception', this, xhr);
26075 var response = Roo.decode(xhr.responseText);
26077 if(!response.success){
26078 this.fireEvent('exception', this, xhr);
26082 var response = Roo.decode(xhr.responseText);
26084 this.fireEvent('upload', this, response);
26088 xhrOnError : function()
26091 this.maskEl.unmask();
26094 Roo.log('xhr on error');
26096 var response = Roo.decode(xhr.responseText);
26102 prepare : function(file)
26105 this.maskEl.mask(this.loadingText);
26111 if(typeof(file) === 'string'){
26112 this.loadCanvas(file);
26116 if(!file || !this.urlAPI){
26121 this.cropType = file.type;
26125 if(this.fireEvent('prepare', this, this.file) != false){
26127 var reader = new FileReader();
26129 reader.onload = function (e) {
26130 if (e.target.error) {
26131 Roo.log(e.target.error);
26135 var buffer = e.target.result,
26136 dataView = new DataView(buffer),
26138 maxOffset = dataView.byteLength - 4,
26142 if (dataView.getUint16(0) === 0xffd8) {
26143 while (offset < maxOffset) {
26144 markerBytes = dataView.getUint16(offset);
26146 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26147 markerLength = dataView.getUint16(offset + 2) + 2;
26148 if (offset + markerLength > dataView.byteLength) {
26149 Roo.log('Invalid meta data: Invalid segment size.');
26153 if(markerBytes == 0xffe1){
26154 _this.parseExifData(
26161 offset += markerLength;
26171 var url = _this.urlAPI.createObjectURL(_this.file);
26173 _this.loadCanvas(url);
26178 reader.readAsArrayBuffer(this.file);
26184 parseExifData : function(dataView, offset, length)
26186 var tiffOffset = offset + 10,
26190 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26191 // No Exif data, might be XMP data instead
26195 // Check for the ASCII code for "Exif" (0x45786966):
26196 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26197 // No Exif data, might be XMP data instead
26200 if (tiffOffset + 8 > dataView.byteLength) {
26201 Roo.log('Invalid Exif data: Invalid segment size.');
26204 // Check for the two null bytes:
26205 if (dataView.getUint16(offset + 8) !== 0x0000) {
26206 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26209 // Check the byte alignment:
26210 switch (dataView.getUint16(tiffOffset)) {
26212 littleEndian = true;
26215 littleEndian = false;
26218 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26221 // Check for the TIFF tag marker (0x002A):
26222 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26223 Roo.log('Invalid Exif data: Missing TIFF marker.');
26226 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26227 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26229 this.parseExifTags(
26232 tiffOffset + dirOffset,
26237 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26242 if (dirOffset + 6 > dataView.byteLength) {
26243 Roo.log('Invalid Exif data: Invalid directory offset.');
26246 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26247 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26248 if (dirEndOffset + 4 > dataView.byteLength) {
26249 Roo.log('Invalid Exif data: Invalid directory size.');
26252 for (i = 0; i < tagsNumber; i += 1) {
26256 dirOffset + 2 + 12 * i, // tag offset
26260 // Return the offset to the next directory:
26261 return dataView.getUint32(dirEndOffset, littleEndian);
26264 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26266 var tag = dataView.getUint16(offset, littleEndian);
26268 this.exif[tag] = this.getExifValue(
26272 dataView.getUint16(offset + 2, littleEndian), // tag type
26273 dataView.getUint32(offset + 4, littleEndian), // tag length
26278 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26280 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26289 Roo.log('Invalid Exif data: Invalid tag type.');
26293 tagSize = tagType.size * length;
26294 // Determine if the value is contained in the dataOffset bytes,
26295 // or if the value at the dataOffset is a pointer to the actual data:
26296 dataOffset = tagSize > 4 ?
26297 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26298 if (dataOffset + tagSize > dataView.byteLength) {
26299 Roo.log('Invalid Exif data: Invalid data offset.');
26302 if (length === 1) {
26303 return tagType.getValue(dataView, dataOffset, littleEndian);
26306 for (i = 0; i < length; i += 1) {
26307 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26310 if (tagType.ascii) {
26312 // Concatenate the chars:
26313 for (i = 0; i < values.length; i += 1) {
26315 // Ignore the terminating NULL byte(s):
26316 if (c === '\u0000') {
26328 Roo.apply(Roo.bootstrap.UploadCropbox, {
26330 'Orientation': 0x0112
26334 1: 0, //'top-left',
26336 3: 180, //'bottom-right',
26337 // 4: 'bottom-left',
26339 6: 90, //'right-top',
26340 // 7: 'right-bottom',
26341 8: 270 //'left-bottom'
26345 // byte, 8-bit unsigned int:
26347 getValue: function (dataView, dataOffset) {
26348 return dataView.getUint8(dataOffset);
26352 // ascii, 8-bit byte:
26354 getValue: function (dataView, dataOffset) {
26355 return String.fromCharCode(dataView.getUint8(dataOffset));
26360 // short, 16 bit int:
26362 getValue: function (dataView, dataOffset, littleEndian) {
26363 return dataView.getUint16(dataOffset, littleEndian);
26367 // long, 32 bit int:
26369 getValue: function (dataView, dataOffset, littleEndian) {
26370 return dataView.getUint32(dataOffset, littleEndian);
26374 // rational = two long values, first is numerator, second is denominator:
26376 getValue: function (dataView, dataOffset, littleEndian) {
26377 return dataView.getUint32(dataOffset, littleEndian) /
26378 dataView.getUint32(dataOffset + 4, littleEndian);
26382 // slong, 32 bit signed int:
26384 getValue: function (dataView, dataOffset, littleEndian) {
26385 return dataView.getInt32(dataOffset, littleEndian);
26389 // srational, two slongs, first is numerator, second is denominator:
26391 getValue: function (dataView, dataOffset, littleEndian) {
26392 return dataView.getInt32(dataOffset, littleEndian) /
26393 dataView.getInt32(dataOffset + 4, littleEndian);
26403 cls : 'btn-group roo-upload-cropbox-rotate-left',
26404 action : 'rotate-left',
26408 cls : 'btn btn-default',
26409 html : '<i class="fa fa-undo"></i>'
26415 cls : 'btn-group roo-upload-cropbox-picture',
26416 action : 'picture',
26420 cls : 'btn btn-default',
26421 html : '<i class="fa fa-picture-o"></i>'
26427 cls : 'btn-group roo-upload-cropbox-rotate-right',
26428 action : 'rotate-right',
26432 cls : 'btn btn-default',
26433 html : '<i class="fa fa-repeat"></i>'
26441 cls : 'btn-group roo-upload-cropbox-rotate-left',
26442 action : 'rotate-left',
26446 cls : 'btn btn-default',
26447 html : '<i class="fa fa-undo"></i>'
26453 cls : 'btn-group roo-upload-cropbox-download',
26454 action : 'download',
26458 cls : 'btn btn-default',
26459 html : '<i class="fa fa-download"></i>'
26465 cls : 'btn-group roo-upload-cropbox-crop',
26470 cls : 'btn btn-default',
26471 html : '<i class="fa fa-crop"></i>'
26477 cls : 'btn-group roo-upload-cropbox-trash',
26482 cls : 'btn btn-default',
26483 html : '<i class="fa fa-trash"></i>'
26489 cls : 'btn-group roo-upload-cropbox-rotate-right',
26490 action : 'rotate-right',
26494 cls : 'btn btn-default',
26495 html : '<i class="fa fa-repeat"></i>'
26503 cls : 'btn-group roo-upload-cropbox-rotate-left',
26504 action : 'rotate-left',
26508 cls : 'btn btn-default',
26509 html : '<i class="fa fa-undo"></i>'
26515 cls : 'btn-group roo-upload-cropbox-rotate-right',
26516 action : 'rotate-right',
26520 cls : 'btn btn-default',
26521 html : '<i class="fa fa-repeat"></i>'
26534 * @class Roo.bootstrap.DocumentManager
26535 * @extends Roo.bootstrap.Component
26536 * Bootstrap DocumentManager class
26537 * @cfg {String} paramName default 'imageUpload'
26538 * @cfg {String} method default POST
26539 * @cfg {String} url action url
26540 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26541 * @cfg {Boolean} multiple multiple upload default true
26542 * @cfg {Number} thumbSize default 300
26543 * @cfg {String} fieldLabel
26544 * @cfg {Number} labelWidth default 4
26545 * @cfg {String} labelAlign (left|top) default left
26546 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26549 * Create a new DocumentManager
26550 * @param {Object} config The config object
26553 Roo.bootstrap.DocumentManager = function(config){
26554 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26559 * Fire when initial the DocumentManager
26560 * @param {Roo.bootstrap.DocumentManager} this
26565 * inspect selected file
26566 * @param {Roo.bootstrap.DocumentManager} this
26567 * @param {File} file
26572 * Fire when xhr load exception
26573 * @param {Roo.bootstrap.DocumentManager} this
26574 * @param {XMLHttpRequest} xhr
26576 "exception" : true,
26579 * prepare the form data
26580 * @param {Roo.bootstrap.DocumentManager} this
26581 * @param {Object} formData
26586 * Fire when remove the file
26587 * @param {Roo.bootstrap.DocumentManager} this
26588 * @param {Object} file
26593 * Fire after refresh the file
26594 * @param {Roo.bootstrap.DocumentManager} this
26599 * Fire after click the image
26600 * @param {Roo.bootstrap.DocumentManager} this
26601 * @param {Object} file
26606 * Fire when upload a image and editable set to true
26607 * @param {Roo.bootstrap.DocumentManager} this
26608 * @param {Object} file
26612 * @event beforeselectfile
26613 * Fire before select file
26614 * @param {Roo.bootstrap.DocumentManager} this
26616 "beforeselectfile" : true,
26619 * Fire before process file
26620 * @param {Roo.bootstrap.DocumentManager} this
26621 * @param {Object} file
26628 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26637 paramName : 'imageUpload',
26640 labelAlign : 'left',
26647 getAutoCreate : function()
26649 var managerWidget = {
26651 cls : 'roo-document-manager',
26655 cls : 'roo-document-manager-selector',
26660 cls : 'roo-document-manager-uploader',
26664 cls : 'roo-document-manager-upload-btn',
26665 html : '<i class="fa fa-plus"></i>'
26676 cls : 'column col-md-12',
26681 if(this.fieldLabel.length){
26686 cls : 'column col-md-12',
26687 html : this.fieldLabel
26691 cls : 'column col-md-12',
26696 if(this.labelAlign == 'left'){
26700 cls : 'column col-md-' + this.labelWidth,
26701 html : this.fieldLabel
26705 cls : 'column col-md-' + (12 - this.labelWidth),
26715 cls : 'row clearfix',
26723 initEvents : function()
26725 this.managerEl = this.el.select('.roo-document-manager', true).first();
26726 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26728 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26729 this.selectorEl.hide();
26732 this.selectorEl.attr('multiple', 'multiple');
26735 this.selectorEl.on('change', this.onFileSelected, this);
26737 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26738 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26740 this.uploader.on('click', this.onUploaderClick, this);
26742 this.renderProgressDialog();
26746 window.addEventListener("resize", function() { _this.refresh(); } );
26748 this.fireEvent('initial', this);
26751 renderProgressDialog : function()
26755 this.progressDialog = new Roo.bootstrap.Modal({
26756 cls : 'roo-document-manager-progress-dialog',
26757 allow_close : false,
26767 btnclick : function() {
26768 _this.uploadCancel();
26774 this.progressDialog.render(Roo.get(document.body));
26776 this.progress = new Roo.bootstrap.Progress({
26777 cls : 'roo-document-manager-progress',
26782 this.progress.render(this.progressDialog.getChildContainer());
26784 this.progressBar = new Roo.bootstrap.ProgressBar({
26785 cls : 'roo-document-manager-progress-bar',
26788 aria_valuemax : 12,
26792 this.progressBar.render(this.progress.getChildContainer());
26795 onUploaderClick : function(e)
26797 e.preventDefault();
26799 if(this.fireEvent('beforeselectfile', this) != false){
26800 this.selectorEl.dom.click();
26805 onFileSelected : function(e)
26807 e.preventDefault();
26809 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26813 Roo.each(this.selectorEl.dom.files, function(file){
26814 if(this.fireEvent('inspect', this, file) != false){
26815 this.files.push(file);
26825 this.selectorEl.dom.value = '';
26827 if(!this.files.length){
26831 if(this.boxes > 0 && this.files.length > this.boxes){
26832 this.files = this.files.slice(0, this.boxes);
26835 this.uploader.show();
26837 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26838 this.uploader.hide();
26847 Roo.each(this.files, function(file){
26849 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26850 var f = this.renderPreview(file);
26855 if(file.type.indexOf('image') != -1){
26856 this.delegates.push(
26858 _this.process(file);
26859 }).createDelegate(this)
26867 _this.process(file);
26868 }).createDelegate(this)
26873 this.files = files;
26875 this.delegates = this.delegates.concat(docs);
26877 if(!this.delegates.length){
26882 this.progressBar.aria_valuemax = this.delegates.length;
26889 arrange : function()
26891 if(!this.delegates.length){
26892 this.progressDialog.hide();
26897 var delegate = this.delegates.shift();
26899 this.progressDialog.show();
26901 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26903 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26908 refresh : function()
26910 this.uploader.show();
26912 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26913 this.uploader.hide();
26916 Roo.isTouch ? this.closable(false) : this.closable(true);
26918 this.fireEvent('refresh', this);
26921 onRemove : function(e, el, o)
26923 e.preventDefault();
26925 this.fireEvent('remove', this, o);
26929 remove : function(o)
26933 Roo.each(this.files, function(file){
26934 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26943 this.files = files;
26950 Roo.each(this.files, function(file){
26955 file.target.remove();
26964 onClick : function(e, el, o)
26966 e.preventDefault();
26968 this.fireEvent('click', this, o);
26972 closable : function(closable)
26974 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26976 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26988 xhrOnLoad : function(xhr)
26990 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26994 if (xhr.readyState !== 4) {
26996 this.fireEvent('exception', this, xhr);
27000 var response = Roo.decode(xhr.responseText);
27002 if(!response.success){
27004 this.fireEvent('exception', this, xhr);
27008 var file = this.renderPreview(response.data);
27010 this.files.push(file);
27016 xhrOnError : function(xhr)
27018 Roo.log('xhr on error');
27020 var response = Roo.decode(xhr.responseText);
27027 process : function(file)
27029 if(this.fireEvent('process', this, file) !== false){
27030 if(this.editable && file.type.indexOf('image') != -1){
27031 this.fireEvent('edit', this, file);
27035 this.uploadStart(file, false);
27042 uploadStart : function(file, crop)
27044 this.xhr = new XMLHttpRequest();
27046 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27051 file.xhr = this.xhr;
27053 this.managerEl.createChild({
27055 cls : 'roo-document-manager-loading',
27059 tooltip : file.name,
27060 cls : 'roo-document-manager-thumb',
27061 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27067 this.xhr.open(this.method, this.url, true);
27070 "Accept": "application/json",
27071 "Cache-Control": "no-cache",
27072 "X-Requested-With": "XMLHttpRequest"
27075 for (var headerName in headers) {
27076 var headerValue = headers[headerName];
27078 this.xhr.setRequestHeader(headerName, headerValue);
27084 this.xhr.onload = function()
27086 _this.xhrOnLoad(_this.xhr);
27089 this.xhr.onerror = function()
27091 _this.xhrOnError(_this.xhr);
27094 var formData = new FormData();
27096 formData.append('returnHTML', 'NO');
27099 formData.append('crop', crop);
27102 formData.append(this.paramName, file, file.name);
27104 if(this.fireEvent('prepare', this, formData) != false){
27105 this.xhr.send(formData);
27109 uploadCancel : function()
27116 this.delegates = [];
27118 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27125 renderPreview : function(file)
27127 if(typeof(file.target) != 'undefined' && file.target){
27131 var previewEl = this.managerEl.createChild({
27133 cls : 'roo-document-manager-preview',
27137 tooltip : file.filename,
27138 cls : 'roo-document-manager-thumb',
27139 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27144 html : '<i class="fa fa-times-circle"></i>'
27149 var close = previewEl.select('button.close', true).first();
27151 close.on('click', this.onRemove, this, file);
27153 file.target = previewEl;
27155 var image = previewEl.select('img', true).first();
27159 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27161 image.on('click', this.onClick, this, file);
27167 onPreviewLoad : function(file, image)
27169 if(typeof(file.target) == 'undefined' || !file.target){
27173 var width = image.dom.naturalWidth || image.dom.width;
27174 var height = image.dom.naturalHeight || image.dom.height;
27176 if(width > height){
27177 file.target.addClass('wide');
27181 file.target.addClass('tall');
27186 uploadFromSource : function(file, crop)
27188 this.xhr = new XMLHttpRequest();
27190 this.managerEl.createChild({
27192 cls : 'roo-document-manager-loading',
27196 tooltip : file.name,
27197 cls : 'roo-document-manager-thumb',
27198 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27204 this.xhr.open(this.method, this.url, true);
27207 "Accept": "application/json",
27208 "Cache-Control": "no-cache",
27209 "X-Requested-With": "XMLHttpRequest"
27212 for (var headerName in headers) {
27213 var headerValue = headers[headerName];
27215 this.xhr.setRequestHeader(headerName, headerValue);
27221 this.xhr.onload = function()
27223 _this.xhrOnLoad(_this.xhr);
27226 this.xhr.onerror = function()
27228 _this.xhrOnError(_this.xhr);
27231 var formData = new FormData();
27233 formData.append('returnHTML', 'NO');
27235 formData.append('crop', crop);
27237 if(typeof(file.filename) != 'undefined'){
27238 formData.append('filename', file.filename);
27241 if(typeof(file.mimetype) != 'undefined'){
27242 formData.append('mimetype', file.mimetype);
27245 if(this.fireEvent('prepare', this, formData) != false){
27246 this.xhr.send(formData);
27256 * @class Roo.bootstrap.DocumentViewer
27257 * @extends Roo.bootstrap.Component
27258 * Bootstrap DocumentViewer class
27261 * Create a new DocumentViewer
27262 * @param {Object} config The config object
27265 Roo.bootstrap.DocumentViewer = function(config){
27266 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27271 * Fire after initEvent
27272 * @param {Roo.bootstrap.DocumentViewer} this
27278 * @param {Roo.bootstrap.DocumentViewer} this
27283 * Fire after trash button
27284 * @param {Roo.bootstrap.DocumentViewer} this
27291 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27293 getAutoCreate : function()
27297 cls : 'roo-document-viewer',
27301 cls : 'roo-document-viewer-body',
27305 cls : 'roo-document-viewer-thumb',
27309 cls : 'roo-document-viewer-image'
27317 cls : 'roo-document-viewer-footer',
27320 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27328 cls : 'btn btn-default roo-document-viewer-trash',
27329 html : '<i class="fa fa-trash"></i>'
27342 initEvents : function()
27345 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27346 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27348 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27349 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27352 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27354 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27355 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27357 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27358 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27360 this.bodyEl.on('click', this.onClick, this);
27362 this.trashBtn.on('click', this.onTrash, this);
27366 initial : function()
27368 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27371 this.fireEvent('initial', this);
27375 onClick : function(e)
27377 e.preventDefault();
27379 this.fireEvent('click', this);
27382 onTrash : function(e)
27384 e.preventDefault();
27386 this.fireEvent('trash', this);
27398 * @class Roo.bootstrap.NavProgressBar
27399 * @extends Roo.bootstrap.Component
27400 * Bootstrap NavProgressBar class
27403 * Create a new nav progress bar
27404 * @param {Object} config The config object
27407 Roo.bootstrap.NavProgressBar = function(config){
27408 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27410 this.bullets = this.bullets || [];
27412 // Roo.bootstrap.NavProgressBar.register(this);
27416 * Fires when the active item changes
27417 * @param {Roo.bootstrap.NavProgressBar} this
27418 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27419 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27426 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27431 getAutoCreate : function()
27433 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27437 cls : 'roo-navigation-bar-group',
27441 cls : 'roo-navigation-top-bar'
27445 cls : 'roo-navigation-bullets-bar',
27449 cls : 'roo-navigation-bar'
27456 cls : 'roo-navigation-bottom-bar'
27466 initEvents: function()
27471 onRender : function(ct, position)
27473 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27475 if(this.bullets.length){
27476 Roo.each(this.bullets, function(b){
27485 addItem : function(cfg)
27487 var item = new Roo.bootstrap.NavProgressItem(cfg);
27489 item.parentId = this.id;
27490 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27493 var top = new Roo.bootstrap.Element({
27495 cls : 'roo-navigation-bar-text'
27498 var bottom = new Roo.bootstrap.Element({
27500 cls : 'roo-navigation-bar-text'
27503 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27504 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27506 var topText = new Roo.bootstrap.Element({
27508 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27511 var bottomText = new Roo.bootstrap.Element({
27513 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27516 topText.onRender(top.el, null);
27517 bottomText.onRender(bottom.el, null);
27520 item.bottomEl = bottom;
27523 this.barItems.push(item);
27528 getActive : function()
27530 var active = false;
27532 Roo.each(this.barItems, function(v){
27534 if (!v.isActive()) {
27546 setActiveItem : function(item)
27550 Roo.each(this.barItems, function(v){
27551 if (v.rid == item.rid) {
27555 if (v.isActive()) {
27556 v.setActive(false);
27561 item.setActive(true);
27563 this.fireEvent('changed', this, item, prev);
27566 getBarItem: function(rid)
27570 Roo.each(this.barItems, function(e) {
27571 if (e.rid != rid) {
27582 indexOfItem : function(item)
27586 Roo.each(this.barItems, function(v, i){
27588 if (v.rid != item.rid) {
27599 setActiveNext : function()
27601 var i = this.indexOfItem(this.getActive());
27603 if (i > this.barItems.length) {
27607 this.setActiveItem(this.barItems[i+1]);
27610 setActivePrev : function()
27612 var i = this.indexOfItem(this.getActive());
27618 this.setActiveItem(this.barItems[i-1]);
27621 format : function()
27623 if(!this.barItems.length){
27627 var width = 100 / this.barItems.length;
27629 Roo.each(this.barItems, function(i){
27630 i.el.setStyle('width', width + '%');
27631 i.topEl.el.setStyle('width', width + '%');
27632 i.bottomEl.el.setStyle('width', width + '%');
27641 * Nav Progress Item
27646 * @class Roo.bootstrap.NavProgressItem
27647 * @extends Roo.bootstrap.Component
27648 * Bootstrap NavProgressItem class
27649 * @cfg {String} rid the reference id
27650 * @cfg {Boolean} active (true|false) Is item active default false
27651 * @cfg {Boolean} disabled (true|false) Is item active default false
27652 * @cfg {String} html
27653 * @cfg {String} position (top|bottom) text position default bottom
27654 * @cfg {String} icon show icon instead of number
27657 * Create a new NavProgressItem
27658 * @param {Object} config The config object
27660 Roo.bootstrap.NavProgressItem = function(config){
27661 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27666 * The raw click event for the entire grid.
27667 * @param {Roo.bootstrap.NavProgressItem} this
27668 * @param {Roo.EventObject} e
27675 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27681 position : 'bottom',
27684 getAutoCreate : function()
27686 var iconCls = 'roo-navigation-bar-item-icon';
27688 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27692 cls: 'roo-navigation-bar-item',
27702 cfg.cls += ' active';
27705 cfg.cls += ' disabled';
27711 disable : function()
27713 this.setDisabled(true);
27716 enable : function()
27718 this.setDisabled(false);
27721 initEvents: function()
27723 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27725 this.iconEl.on('click', this.onClick, this);
27728 onClick : function(e)
27730 e.preventDefault();
27736 if(this.fireEvent('click', this, e) === false){
27740 this.parent().setActiveItem(this);
27743 isActive: function ()
27745 return this.active;
27748 setActive : function(state)
27750 if(this.active == state){
27754 this.active = state;
27757 this.el.addClass('active');
27761 this.el.removeClass('active');
27766 setDisabled : function(state)
27768 if(this.disabled == state){
27772 this.disabled = state;
27775 this.el.addClass('disabled');
27779 this.el.removeClass('disabled');
27782 tooltipEl : function()
27784 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27797 * @class Roo.bootstrap.FieldLabel
27798 * @extends Roo.bootstrap.Component
27799 * Bootstrap FieldLabel class
27800 * @cfg {String} html contents of the element
27801 * @cfg {String} tag tag of the element default label
27802 * @cfg {String} cls class of the element
27803 * @cfg {String} target label target
27804 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27805 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27806 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27807 * @cfg {String} iconTooltip default "This field is required"
27810 * Create a new FieldLabel
27811 * @param {Object} config The config object
27814 Roo.bootstrap.FieldLabel = function(config){
27815 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27820 * Fires after the field has been marked as invalid.
27821 * @param {Roo.form.FieldLabel} this
27822 * @param {String} msg The validation message
27827 * Fires after the field has been validated with no errors.
27828 * @param {Roo.form.FieldLabel} this
27834 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27841 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27842 validClass : 'text-success fa fa-lg fa-check',
27843 iconTooltip : 'This field is required',
27845 getAutoCreate : function(){
27849 cls : 'roo-bootstrap-field-label ' + this.cls,
27855 tooltip : this.iconTooltip
27867 initEvents: function()
27869 Roo.bootstrap.Element.superclass.initEvents.call(this);
27871 this.iconEl = this.el.select('i', true).first();
27873 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27875 Roo.bootstrap.FieldLabel.register(this);
27879 * Mark this field as valid
27881 markValid : function()
27883 this.iconEl.show();
27885 this.iconEl.removeClass(this.invalidClass);
27887 this.iconEl.addClass(this.validClass);
27889 this.fireEvent('valid', this);
27893 * Mark this field as invalid
27894 * @param {String} msg The validation message
27896 markInvalid : function(msg)
27898 this.iconEl.show();
27900 this.iconEl.removeClass(this.validClass);
27902 this.iconEl.addClass(this.invalidClass);
27904 this.fireEvent('invalid', this, msg);
27910 Roo.apply(Roo.bootstrap.FieldLabel, {
27915 * register a FieldLabel Group
27916 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27918 register : function(label)
27920 if(this.groups.hasOwnProperty(label.target)){
27924 this.groups[label.target] = label;
27928 * fetch a FieldLabel Group based on the target
27929 * @param {string} target
27930 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27932 get: function(target) {
27933 if (typeof(this.groups[target]) == 'undefined') {
27937 return this.groups[target] ;
27946 * page DateSplitField.
27952 * @class Roo.bootstrap.DateSplitField
27953 * @extends Roo.bootstrap.Component
27954 * Bootstrap DateSplitField class
27955 * @cfg {string} fieldLabel - the label associated
27956 * @cfg {Number} labelWidth set the width of label (0-12)
27957 * @cfg {String} labelAlign (top|left)
27958 * @cfg {Boolean} dayAllowBlank (true|false) default false
27959 * @cfg {Boolean} monthAllowBlank (true|false) default false
27960 * @cfg {Boolean} yearAllowBlank (true|false) default false
27961 * @cfg {string} dayPlaceholder
27962 * @cfg {string} monthPlaceholder
27963 * @cfg {string} yearPlaceholder
27964 * @cfg {string} dayFormat default 'd'
27965 * @cfg {string} monthFormat default 'm'
27966 * @cfg {string} yearFormat default 'Y'
27970 * Create a new DateSplitField
27971 * @param {Object} config The config object
27974 Roo.bootstrap.DateSplitField = function(config){
27975 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27981 * getting the data of years
27982 * @param {Roo.bootstrap.DateSplitField} this
27983 * @param {Object} years
27988 * getting the data of days
27989 * @param {Roo.bootstrap.DateSplitField} this
27990 * @param {Object} days
27995 * Fires after the field has been marked as invalid.
27996 * @param {Roo.form.Field} this
27997 * @param {String} msg The validation message
28002 * Fires after the field has been validated with no errors.
28003 * @param {Roo.form.Field} this
28009 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28012 labelAlign : 'top',
28014 dayAllowBlank : false,
28015 monthAllowBlank : false,
28016 yearAllowBlank : false,
28017 dayPlaceholder : '',
28018 monthPlaceholder : '',
28019 yearPlaceholder : '',
28023 isFormField : true,
28025 getAutoCreate : function()
28029 cls : 'row roo-date-split-field-group',
28034 cls : 'form-hidden-field roo-date-split-field-group-value',
28040 if(this.fieldLabel){
28043 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28047 html : this.fieldLabel
28053 Roo.each(['day', 'month', 'year'], function(t){
28056 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28063 inputEl: function ()
28065 return this.el.select('.roo-date-split-field-group-value', true).first();
28068 onRender : function(ct, position)
28072 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28074 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28076 this.dayField = new Roo.bootstrap.ComboBox({
28077 allowBlank : this.dayAllowBlank,
28078 alwaysQuery : true,
28079 displayField : 'value',
28082 forceSelection : true,
28084 placeholder : this.dayPlaceholder,
28085 selectOnFocus : true,
28086 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28087 triggerAction : 'all',
28089 valueField : 'value',
28090 store : new Roo.data.SimpleStore({
28091 data : (function() {
28093 _this.fireEvent('days', _this, days);
28096 fields : [ 'value' ]
28099 select : function (_self, record, index)
28101 _this.setValue(_this.getValue());
28106 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28108 this.monthField = new Roo.bootstrap.MonthField({
28109 after : '<i class=\"fa fa-calendar\"></i>',
28110 allowBlank : this.monthAllowBlank,
28111 placeholder : this.monthPlaceholder,
28114 render : function (_self)
28116 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28117 e.preventDefault();
28121 select : function (_self, oldvalue, newvalue)
28123 _this.setValue(_this.getValue());
28128 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28130 this.yearField = new Roo.bootstrap.ComboBox({
28131 allowBlank : this.yearAllowBlank,
28132 alwaysQuery : true,
28133 displayField : 'value',
28136 forceSelection : true,
28138 placeholder : this.yearPlaceholder,
28139 selectOnFocus : true,
28140 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28141 triggerAction : 'all',
28143 valueField : 'value',
28144 store : new Roo.data.SimpleStore({
28145 data : (function() {
28147 _this.fireEvent('years', _this, years);
28150 fields : [ 'value' ]
28153 select : function (_self, record, index)
28155 _this.setValue(_this.getValue());
28160 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28163 setValue : function(v, format)
28165 this.inputEl.dom.value = v;
28167 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28169 var d = Date.parseDate(v, f);
28176 this.setDay(d.format(this.dayFormat));
28177 this.setMonth(d.format(this.monthFormat));
28178 this.setYear(d.format(this.yearFormat));
28185 setDay : function(v)
28187 this.dayField.setValue(v);
28188 this.inputEl.dom.value = this.getValue();
28193 setMonth : function(v)
28195 this.monthField.setValue(v, true);
28196 this.inputEl.dom.value = this.getValue();
28201 setYear : function(v)
28203 this.yearField.setValue(v);
28204 this.inputEl.dom.value = this.getValue();
28209 getDay : function()
28211 return this.dayField.getValue();
28214 getMonth : function()
28216 return this.monthField.getValue();
28219 getYear : function()
28221 return this.yearField.getValue();
28224 getValue : function()
28226 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28228 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28238 this.inputEl.dom.value = '';
28243 validate : function()
28245 var d = this.dayField.validate();
28246 var m = this.monthField.validate();
28247 var y = this.yearField.validate();
28252 (!this.dayAllowBlank && !d) ||
28253 (!this.monthAllowBlank && !m) ||
28254 (!this.yearAllowBlank && !y)
28259 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28268 this.markInvalid();
28273 markValid : function()
28276 var label = this.el.select('label', true).first();
28277 var icon = this.el.select('i.fa-star', true).first();
28283 this.fireEvent('valid', this);
28287 * Mark this field as invalid
28288 * @param {String} msg The validation message
28290 markInvalid : function(msg)
28293 var label = this.el.select('label', true).first();
28294 var icon = this.el.select('i.fa-star', true).first();
28296 if(label && !icon){
28297 this.el.select('.roo-date-split-field-label', true).createChild({
28299 cls : 'text-danger fa fa-lg fa-star',
28300 tooltip : 'This field is required',
28301 style : 'margin-right:5px;'
28305 this.fireEvent('invalid', this, msg);
28308 clearInvalid : function()
28310 var label = this.el.select('label', true).first();
28311 var icon = this.el.select('i.fa-star', true).first();
28317 this.fireEvent('valid', this);
28320 getName: function()
28330 * http://masonry.desandro.com
28332 * The idea is to render all the bricks based on vertical width...
28334 * The original code extends 'outlayer' - we might need to use that....
28340 * @class Roo.bootstrap.LayoutMasonry
28341 * @extends Roo.bootstrap.Component
28342 * Bootstrap Layout Masonry class
28345 * Create a new Element
28346 * @param {Object} config The config object
28349 Roo.bootstrap.LayoutMasonry = function(config){
28350 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28356 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28359 * @cfg {Boolean} isLayoutInstant = no animation?
28361 isLayoutInstant : false, // needed?
28364 * @cfg {Number} boxWidth width of the columns
28369 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28374 * @cfg {Number} padWidth padding below box..
28379 * @cfg {Number} gutter gutter width..
28384 * @cfg {Boolean} isAutoInitial defalut true
28386 isAutoInitial : true,
28391 * @cfg {Boolean} isHorizontal defalut false
28393 isHorizontal : false,
28395 currentSize : null,
28401 bricks: null, //CompositeElement
28405 _isLayoutInited : false,
28407 // isAlternative : false, // only use for vertical layout...
28410 * @cfg {Number} alternativePadWidth padding below box..
28412 alternativePadWidth : 50,
28414 getAutoCreate : function(){
28418 cls: 'blog-masonary-wrapper ' + this.cls,
28420 cls : 'mas-boxes masonary'
28427 getChildContainer: function( )
28429 if (this.boxesEl) {
28430 return this.boxesEl;
28433 this.boxesEl = this.el.select('.mas-boxes').first();
28435 return this.boxesEl;
28439 initEvents : function()
28443 if(this.isAutoInitial){
28444 Roo.log('hook children rendered');
28445 this.on('childrenrendered', function() {
28446 Roo.log('children rendered');
28452 initial : function()
28454 this.currentSize = this.el.getBox(true);
28456 Roo.EventManager.onWindowResize(this.resize, this);
28458 if(!this.isAutoInitial){
28466 //this.layout.defer(500,this);
28470 resize : function()
28474 var cs = this.el.getBox(true);
28476 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28477 Roo.log("no change in with or X");
28481 this.currentSize = cs;
28487 layout : function()
28489 this._resetLayout();
28491 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28493 this.layoutItems( isInstant );
28495 this._isLayoutInited = true;
28499 _resetLayout : function()
28501 if(this.isHorizontal){
28502 this.horizontalMeasureColumns();
28506 this.verticalMeasureColumns();
28510 verticalMeasureColumns : function()
28512 this.getContainerWidth();
28514 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28515 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28519 var boxWidth = this.boxWidth + this.padWidth;
28521 if(this.containerWidth < this.boxWidth){
28522 boxWidth = this.containerWidth
28525 var containerWidth = this.containerWidth;
28527 var cols = Math.floor(containerWidth / boxWidth);
28529 this.cols = Math.max( cols, 1 );
28531 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28533 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28535 this.colWidth = boxWidth + avail - this.padWidth;
28537 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28538 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28541 horizontalMeasureColumns : function()
28543 this.getContainerWidth();
28545 var boxWidth = this.boxWidth;
28547 if(this.containerWidth < boxWidth){
28548 boxWidth = this.containerWidth;
28551 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28553 this.el.setHeight(boxWidth);
28557 getContainerWidth : function()
28559 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28562 layoutItems : function( isInstant )
28564 var items = Roo.apply([], this.bricks);
28566 if(this.isHorizontal){
28567 this._horizontalLayoutItems( items , isInstant );
28571 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28572 // this._verticalAlternativeLayoutItems( items , isInstant );
28576 this._verticalLayoutItems( items , isInstant );
28580 _verticalLayoutItems : function ( items , isInstant)
28582 if ( !items || !items.length ) {
28587 ['xs', 'xs', 'xs', 'tall'],
28588 ['xs', 'xs', 'tall'],
28589 ['xs', 'xs', 'sm'],
28590 ['xs', 'xs', 'xs'],
28596 ['sm', 'xs', 'xs'],
28600 ['tall', 'xs', 'xs', 'xs'],
28601 ['tall', 'xs', 'xs'],
28613 Roo.each(items, function(item, k){
28615 switch (item.size) {
28616 // these layouts take up a full box,
28627 boxes.push([item]);
28650 var filterPattern = function(box, length)
28658 var pattern = box.slice(0, length);
28662 Roo.each(pattern, function(i){
28663 format.push(i.size);
28666 Roo.each(standard, function(s){
28668 if(String(s) != String(format)){
28677 if(!match && length == 1){
28682 filterPattern(box, length - 1);
28686 queue.push(pattern);
28688 box = box.slice(length, box.length);
28690 filterPattern(box, 4);
28696 Roo.each(boxes, function(box, k){
28702 if(box.length == 1){
28707 filterPattern(box, 4);
28711 this._processVerticalLayoutQueue( queue, isInstant );
28715 // _verticalAlternativeLayoutItems : function( items , isInstant )
28717 // if ( !items || !items.length ) {
28721 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28725 _horizontalLayoutItems : function ( items , isInstant)
28727 if ( !items || !items.length || items.length < 3) {
28733 var eItems = items.slice(0, 3);
28735 items = items.slice(3, items.length);
28738 ['xs', 'xs', 'xs', 'wide'],
28739 ['xs', 'xs', 'wide'],
28740 ['xs', 'xs', 'sm'],
28741 ['xs', 'xs', 'xs'],
28747 ['sm', 'xs', 'xs'],
28751 ['wide', 'xs', 'xs', 'xs'],
28752 ['wide', 'xs', 'xs'],
28765 Roo.each(items, function(item, k){
28767 switch (item.size) {
28778 boxes.push([item]);
28802 var filterPattern = function(box, length)
28810 var pattern = box.slice(0, length);
28814 Roo.each(pattern, function(i){
28815 format.push(i.size);
28818 Roo.each(standard, function(s){
28820 if(String(s) != String(format)){
28829 if(!match && length == 1){
28834 filterPattern(box, length - 1);
28838 queue.push(pattern);
28840 box = box.slice(length, box.length);
28842 filterPattern(box, 4);
28848 Roo.each(boxes, function(box, k){
28854 if(box.length == 1){
28859 filterPattern(box, 4);
28866 var pos = this.el.getBox(true);
28870 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28872 var hit_end = false;
28874 Roo.each(queue, function(box){
28878 Roo.each(box, function(b){
28880 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28890 Roo.each(box, function(b){
28892 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28895 mx = Math.max(mx, b.x);
28899 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28903 Roo.each(box, function(b){
28905 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28919 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28922 /** Sets position of item in DOM
28923 * @param {Element} item
28924 * @param {Number} x - horizontal position
28925 * @param {Number} y - vertical position
28926 * @param {Boolean} isInstant - disables transitions
28928 _processVerticalLayoutQueue : function( queue, isInstant )
28930 var pos = this.el.getBox(true);
28935 for (var i = 0; i < this.cols; i++){
28939 Roo.each(queue, function(box, k){
28941 var col = k % this.cols;
28943 Roo.each(box, function(b,kk){
28945 b.el.position('absolute');
28947 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
28948 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
28950 if(b.size == 'md-left' || b.size == 'md-right'){
28951 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
28952 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
28955 b.el.setWidth(width);
28956 b.el.setHeight(height);
28960 for (var i = 0; i < this.cols; i++){
28962 if(maxY[i] < maxY[col]){
28967 col = Math.min(col, i);
28971 x = pos.x + col * (this.colWidth + this.padWidth);
28975 var positions = [];
28977 switch (box.length){
28979 positions = this.getVerticalOneBoxColPositions(x, y, box);
28982 positions = this.getVerticalTwoBoxColPositions(x, y, box);
28985 positions = this.getVerticalThreeBoxColPositions(x, y, box);
28988 positions = this.getVerticalFourBoxColPositions(x, y, box);
28994 Roo.each(box, function(b,kk){
28996 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
28998 var sz = b.el.getSize();
29000 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29008 for (var i = 0; i < this.cols; i++){
29009 mY = Math.max(mY, maxY[i]);
29012 this.el.setHeight(mY - pos.y);
29016 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29018 // var pos = this.el.getBox(true);
29021 // var maxX = pos.right;
29023 // var maxHeight = 0;
29025 // Roo.each(items, function(item, k){
29029 // item.el.position('absolute');
29031 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29033 // item.el.setWidth(width);
29035 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29037 // item.el.setHeight(height);
29040 // item.el.setXY([x, y], isInstant ? false : true);
29042 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29045 // y = y + height + this.alternativePadWidth;
29047 // maxHeight = maxHeight + height + this.alternativePadWidth;
29051 // this.el.setHeight(maxHeight);
29055 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29057 var pos = this.el.getBox(true);
29062 var maxX = pos.right;
29064 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29066 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29068 Roo.each(queue, function(box, k){
29070 Roo.each(box, function(b, kk){
29072 b.el.position('absolute');
29074 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29075 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29077 if(b.size == 'md-left' || b.size == 'md-right'){
29078 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29079 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29082 b.el.setWidth(width);
29083 b.el.setHeight(height);
29091 var positions = [];
29093 switch (box.length){
29095 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29098 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29101 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29104 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29110 Roo.each(box, function(b,kk){
29112 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29114 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29122 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29124 Roo.each(eItems, function(b,k){
29126 b.size = (k == 0) ? 'sm' : 'xs';
29127 b.x = (k == 0) ? 2 : 1;
29128 b.y = (k == 0) ? 2 : 1;
29130 b.el.position('absolute');
29132 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29134 b.el.setWidth(width);
29136 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29138 b.el.setHeight(height);
29142 var positions = [];
29145 x : maxX - this.unitWidth * 2 - this.gutter,
29150 x : maxX - this.unitWidth,
29151 y : minY + (this.unitWidth + this.gutter) * 2
29155 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29159 Roo.each(eItems, function(b,k){
29161 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29167 getVerticalOneBoxColPositions : function(x, y, box)
29171 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29173 if(box[0].size == 'md-left'){
29177 if(box[0].size == 'md-right'){
29182 x : x + (this.unitWidth + this.gutter) * rand,
29189 getVerticalTwoBoxColPositions : function(x, y, box)
29193 if(box[0].size == 'xs'){
29197 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29201 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29215 x : x + (this.unitWidth + this.gutter) * 2,
29216 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29223 getVerticalThreeBoxColPositions : function(x, y, box)
29227 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29235 x : x + (this.unitWidth + this.gutter) * 1,
29240 x : x + (this.unitWidth + this.gutter) * 2,
29248 if(box[0].size == 'xs' && box[1].size == 'xs'){
29257 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29261 x : x + (this.unitWidth + this.gutter) * 1,
29275 x : x + (this.unitWidth + this.gutter) * 2,
29280 x : x + (this.unitWidth + this.gutter) * 2,
29281 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29288 getVerticalFourBoxColPositions : function(x, y, box)
29292 if(box[0].size == 'xs'){
29301 y : y + (this.unitHeight + this.gutter) * 1
29306 y : y + (this.unitHeight + this.gutter) * 2
29310 x : x + (this.unitWidth + this.gutter) * 1,
29324 x : x + (this.unitWidth + this.gutter) * 2,
29329 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29330 y : y + (this.unitHeight + this.gutter) * 1
29334 x : x + (this.unitWidth + this.gutter) * 2,
29335 y : y + (this.unitWidth + this.gutter) * 2
29342 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29346 if(box[0].size == 'md-left'){
29348 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29355 if(box[0].size == 'md-right'){
29357 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29358 y : minY + (this.unitWidth + this.gutter) * 1
29364 var rand = Math.floor(Math.random() * (4 - box[0].y));
29367 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29368 y : minY + (this.unitWidth + this.gutter) * rand
29375 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29379 if(box[0].size == 'xs'){
29382 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29387 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29388 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29396 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29401 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29402 y : minY + (this.unitWidth + this.gutter) * 2
29409 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29413 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29416 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29422 y : minY + (this.unitWidth + this.gutter) * 1
29426 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29427 y : minY + (this.unitWidth + this.gutter) * 2
29434 if(box[0].size == 'xs' && box[1].size == 'xs'){
29437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29442 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29447 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29448 y : minY + (this.unitWidth + this.gutter) * 1
29456 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29461 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29462 y : minY + (this.unitWidth + this.gutter) * 2
29466 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29467 y : minY + (this.unitWidth + this.gutter) * 2
29474 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29478 if(box[0].size == 'xs'){
29481 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29486 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29491 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),
29496 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29497 y : minY + (this.unitWidth + this.gutter) * 1
29505 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29510 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29511 y : minY + (this.unitWidth + this.gutter) * 2
29515 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29516 y : minY + (this.unitWidth + this.gutter) * 2
29520 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),
29521 y : minY + (this.unitWidth + this.gutter) * 2
29535 * http://masonry.desandro.com
29537 * The idea is to render all the bricks based on vertical width...
29539 * The original code extends 'outlayer' - we might need to use that....
29545 * @class Roo.bootstrap.LayoutMasonryAuto
29546 * @extends Roo.bootstrap.Component
29547 * Bootstrap Layout Masonry class
29550 * Create a new Element
29551 * @param {Object} config The config object
29554 Roo.bootstrap.LayoutMasonryAuto = function(config){
29555 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29558 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29561 * @cfg {Boolean} isFitWidth - resize the width..
29563 isFitWidth : false, // options..
29565 * @cfg {Boolean} isOriginLeft = left align?
29567 isOriginLeft : true,
29569 * @cfg {Boolean} isOriginTop = top align?
29571 isOriginTop : false,
29573 * @cfg {Boolean} isLayoutInstant = no animation?
29575 isLayoutInstant : false, // needed?
29577 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29579 isResizingContainer : true,
29581 * @cfg {Number} columnWidth width of the columns
29586 * @cfg {Number} padHeight padding below box..
29592 * @cfg {Boolean} isAutoInitial defalut true
29595 isAutoInitial : true,
29601 initialColumnWidth : 0,
29602 currentSize : null,
29604 colYs : null, // array.
29611 bricks: null, //CompositeElement
29612 cols : 0, // array?
29613 // element : null, // wrapped now this.el
29614 _isLayoutInited : null,
29617 getAutoCreate : function(){
29621 cls: 'blog-masonary-wrapper ' + this.cls,
29623 cls : 'mas-boxes masonary'
29630 getChildContainer: function( )
29632 if (this.boxesEl) {
29633 return this.boxesEl;
29636 this.boxesEl = this.el.select('.mas-boxes').first();
29638 return this.boxesEl;
29642 initEvents : function()
29646 if(this.isAutoInitial){
29647 Roo.log('hook children rendered');
29648 this.on('childrenrendered', function() {
29649 Roo.log('children rendered');
29656 initial : function()
29658 this.reloadItems();
29660 this.currentSize = this.el.getBox(true);
29662 /// was window resize... - let's see if this works..
29663 Roo.EventManager.onWindowResize(this.resize, this);
29665 if(!this.isAutoInitial){
29670 this.layout.defer(500,this);
29673 reloadItems: function()
29675 this.bricks = this.el.select('.masonry-brick', true);
29677 this.bricks.each(function(b) {
29678 //Roo.log(b.getSize());
29679 if (!b.attr('originalwidth')) {
29680 b.attr('originalwidth', b.getSize().width);
29685 Roo.log(this.bricks.elements.length);
29688 resize : function()
29691 var cs = this.el.getBox(true);
29693 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29694 Roo.log("no change in with or X");
29697 this.currentSize = cs;
29701 layout : function()
29704 this._resetLayout();
29705 //this._manageStamps();
29707 // don't animate first layout
29708 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29709 this.layoutItems( isInstant );
29711 // flag for initalized
29712 this._isLayoutInited = true;
29715 layoutItems : function( isInstant )
29717 //var items = this._getItemsForLayout( this.items );
29718 // original code supports filtering layout items.. we just ignore it..
29720 this._layoutItems( this.bricks , isInstant );
29722 this._postLayout();
29724 _layoutItems : function ( items , isInstant)
29726 //this.fireEvent( 'layout', this, items );
29729 if ( !items || !items.elements.length ) {
29730 // no items, emit event with empty array
29735 items.each(function(item) {
29736 Roo.log("layout item");
29738 // get x/y object from method
29739 var position = this._getItemLayoutPosition( item );
29741 position.item = item;
29742 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29743 queue.push( position );
29746 this._processLayoutQueue( queue );
29748 /** Sets position of item in DOM
29749 * @param {Element} item
29750 * @param {Number} x - horizontal position
29751 * @param {Number} y - vertical position
29752 * @param {Boolean} isInstant - disables transitions
29754 _processLayoutQueue : function( queue )
29756 for ( var i=0, len = queue.length; i < len; i++ ) {
29757 var obj = queue[i];
29758 obj.item.position('absolute');
29759 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29765 * Any logic you want to do after each layout,
29766 * i.e. size the container
29768 _postLayout : function()
29770 this.resizeContainer();
29773 resizeContainer : function()
29775 if ( !this.isResizingContainer ) {
29778 var size = this._getContainerSize();
29780 this.el.setSize(size.width,size.height);
29781 this.boxesEl.setSize(size.width,size.height);
29787 _resetLayout : function()
29789 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29790 this.colWidth = this.el.getWidth();
29791 //this.gutter = this.el.getWidth();
29793 this.measureColumns();
29799 this.colYs.push( 0 );
29805 measureColumns : function()
29807 this.getContainerWidth();
29808 // if columnWidth is 0, default to outerWidth of first item
29809 if ( !this.columnWidth ) {
29810 var firstItem = this.bricks.first();
29811 Roo.log(firstItem);
29812 this.columnWidth = this.containerWidth;
29813 if (firstItem && firstItem.attr('originalwidth') ) {
29814 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29816 // columnWidth fall back to item of first element
29817 Roo.log("set column width?");
29818 this.initialColumnWidth = this.columnWidth ;
29820 // if first elem has no width, default to size of container
29825 if (this.initialColumnWidth) {
29826 this.columnWidth = this.initialColumnWidth;
29831 // column width is fixed at the top - however if container width get's smaller we should
29834 // this bit calcs how man columns..
29836 var columnWidth = this.columnWidth += this.gutter;
29838 // calculate columns
29839 var containerWidth = this.containerWidth + this.gutter;
29841 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29842 // fix rounding errors, typically with gutters
29843 var excess = columnWidth - containerWidth % columnWidth;
29846 // if overshoot is less than a pixel, round up, otherwise floor it
29847 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29848 cols = Math[ mathMethod ]( cols );
29849 this.cols = Math.max( cols, 1 );
29852 // padding positioning..
29853 var totalColWidth = this.cols * this.columnWidth;
29854 var padavail = this.containerWidth - totalColWidth;
29855 // so for 2 columns - we need 3 'pads'
29857 var padNeeded = (1+this.cols) * this.padWidth;
29859 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29861 this.columnWidth += padExtra
29862 //this.padWidth = Math.floor(padavail / ( this.cols));
29864 // adjust colum width so that padding is fixed??
29866 // we have 3 columns ... total = width * 3
29867 // we have X left over... that should be used by
29869 //if (this.expandC) {
29877 getContainerWidth : function()
29879 /* // container is parent if fit width
29880 var container = this.isFitWidth ? this.element.parentNode : this.element;
29881 // check that this.size and size are there
29882 // IE8 triggers resize on body size change, so they might not be
29884 var size = getSize( container ); //FIXME
29885 this.containerWidth = size && size.innerWidth; //FIXME
29888 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29892 _getItemLayoutPosition : function( item ) // what is item?
29894 // we resize the item to our columnWidth..
29896 item.setWidth(this.columnWidth);
29897 item.autoBoxAdjust = false;
29899 var sz = item.getSize();
29901 // how many columns does this brick span
29902 var remainder = this.containerWidth % this.columnWidth;
29904 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29905 // round if off by 1 pixel, otherwise use ceil
29906 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
29907 colSpan = Math.min( colSpan, this.cols );
29909 // normally this should be '1' as we dont' currently allow multi width columns..
29911 var colGroup = this._getColGroup( colSpan );
29912 // get the minimum Y value from the columns
29913 var minimumY = Math.min.apply( Math, colGroup );
29914 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
29916 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
29918 // position the brick
29920 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29921 y: this.currentSize.y + minimumY + this.padHeight
29925 // apply setHeight to necessary columns
29926 var setHeight = minimumY + sz.height + this.padHeight;
29927 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
29929 var setSpan = this.cols + 1 - colGroup.length;
29930 for ( var i = 0; i < setSpan; i++ ) {
29931 this.colYs[ shortColIndex + i ] = setHeight ;
29938 * @param {Number} colSpan - number of columns the element spans
29939 * @returns {Array} colGroup
29941 _getColGroup : function( colSpan )
29943 if ( colSpan < 2 ) {
29944 // if brick spans only one column, use all the column Ys
29949 // how many different places could this brick fit horizontally
29950 var groupCount = this.cols + 1 - colSpan;
29951 // for each group potential horizontal position
29952 for ( var i = 0; i < groupCount; i++ ) {
29953 // make an array of colY values for that one group
29954 var groupColYs = this.colYs.slice( i, i + colSpan );
29955 // and get the max value of the array
29956 colGroup[i] = Math.max.apply( Math, groupColYs );
29961 _manageStamp : function( stamp )
29963 var stampSize = stamp.getSize();
29964 var offset = stamp.getBox();
29965 // get the columns that this stamp affects
29966 var firstX = this.isOriginLeft ? offset.x : offset.right;
29967 var lastX = firstX + stampSize.width;
29968 var firstCol = Math.floor( firstX / this.columnWidth );
29969 firstCol = Math.max( 0, firstCol );
29971 var lastCol = Math.floor( lastX / this.columnWidth );
29972 // lastCol should not go over if multiple of columnWidth #425
29973 lastCol -= lastX % this.columnWidth ? 0 : 1;
29974 lastCol = Math.min( this.cols - 1, lastCol );
29976 // set colYs to bottom of the stamp
29977 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
29980 for ( var i = firstCol; i <= lastCol; i++ ) {
29981 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
29986 _getContainerSize : function()
29988 this.maxY = Math.max.apply( Math, this.colYs );
29993 if ( this.isFitWidth ) {
29994 size.width = this._getContainerFitWidth();
30000 _getContainerFitWidth : function()
30002 var unusedCols = 0;
30003 // count unused columns
30006 if ( this.colYs[i] !== 0 ) {
30011 // fit container to columns that have been used
30012 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30015 needsResizeLayout : function()
30017 var previousWidth = this.containerWidth;
30018 this.getContainerWidth();
30019 return previousWidth !== this.containerWidth;
30034 * @class Roo.bootstrap.MasonryBrick
30035 * @extends Roo.bootstrap.Component
30036 * Bootstrap MasonryBrick class
30039 * Create a new MasonryBrick
30040 * @param {Object} config The config object
30043 Roo.bootstrap.MasonryBrick = function(config){
30044 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30050 * When a MasonryBrick is clcik
30051 * @param {Roo.bootstrap.MasonryBrick} this
30052 * @param {Roo.EventObject} e
30058 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30061 * @cfg {String} title
30065 * @cfg {String} html
30069 * @cfg {String} bgimage
30073 * @cfg {String} cls
30077 * @cfg {String} href
30081 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30086 * @cfg {String} (center|bottom) placetitle
30090 getAutoCreate : function()
30092 var cls = 'masonry-brick';
30094 if(this.href.length){
30095 cls += ' masonry-brick-link';
30098 if(this.bgimage.length){
30099 cls += ' masonry-brick-image';
30103 cls += ' masonry-' + this.size + '-brick';
30106 if(this.placetitle.length){
30108 switch (this.placetitle) {
30110 cls += ' masonry-center-title';
30113 cls += ' masonry-bottom-title';
30120 if(!this.html.length && !this.bgimage.length){
30121 cls += ' masonry-center-title';
30124 if(!this.html.length && this.bgimage.length){
30125 cls += ' masonry-bottom-title';
30130 cls += ' ' + this.cls;
30134 tag: (this.href.length) ? 'a' : 'div',
30139 cls: 'masonry-brick-paragraph',
30145 if(this.href.length){
30146 cfg.href = this.href;
30149 var cn = cfg.cn[0].cn;
30151 if(this.title.length){
30154 cls: 'masonry-brick-title',
30159 if(this.html.length){
30162 cls: 'masonry-brick-text',
30167 if(this.bgimage.length){
30170 cls: 'masonry-brick-image-view',
30179 initEvents: function()
30181 switch (this.size) {
30183 // this.intSize = 1;
30188 // this.intSize = 2;
30195 // this.intSize = 3;
30200 // this.intSize = 3;
30205 // this.intSize = 3;
30210 // this.intSize = 3;
30222 this.el.on('touchstart', this.onTouchStart, this);
30223 this.el.on('touchmove', this.onTouchMove, this);
30224 this.el.on('touchend', this.onTouchEnd, this);
30226 this.el.on('mouseenter' ,this.enter, this);
30227 this.el.on('mouseleave', this.leave, this);
30230 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30231 this.parent().bricks.push(this);
30236 onClick: function(e, el)
30244 var time = this.endTimer - this.startTimer;
30252 e.preventDefault();
30255 enter: function(e, el)
30257 e.preventDefault();
30259 if(this.bgimage.length && this.html.length){
30260 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30264 leave: function(e, el)
30266 e.preventDefault();
30268 if(this.bgimage.length && this.html.length){
30269 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30273 onTouchStart: function(e, el)
30275 // e.preventDefault();
30277 if(!this.bgimage.length || !this.html.length){
30281 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30283 this.timer = new Date().getTime();
30285 this.touchmoved = false;
30288 onTouchMove: function(e, el)
30290 this.touchmoved = true;
30293 onTouchEnd: function(e, el)
30295 // e.preventDefault();
30297 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30301 if(!this.bgimage.length || !this.html.length){
30303 if(this.href.length){
30304 window.location.href = this.href;
30310 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30312 window.location.href = this.href;
30327 * @class Roo.bootstrap.Brick
30328 * @extends Roo.bootstrap.Component
30329 * Bootstrap Brick class
30332 * Create a new Brick
30333 * @param {Object} config The config object
30336 Roo.bootstrap.Brick = function(config){
30337 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30343 * When a Brick is click
30344 * @param {Roo.bootstrap.Brick} this
30345 * @param {Roo.EventObject} e
30351 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30354 * @cfg {String} title
30358 * @cfg {String} html
30362 * @cfg {String} bgimage
30366 * @cfg {String} cls
30370 * @cfg {String} href
30374 * @cfg {String} video
30378 * @cfg {Boolean} square
30382 getAutoCreate : function()
30384 var cls = 'roo-brick';
30386 if(this.href.length){
30387 cls += ' roo-brick-link';
30390 if(this.bgimage.length){
30391 cls += ' roo-brick-image';
30394 if(!this.html.length && !this.bgimage.length){
30395 cls += ' roo-brick-center-title';
30398 if(!this.html.length && this.bgimage.length){
30399 cls += ' roo-brick-bottom-title';
30403 cls += ' ' + this.cls;
30407 tag: (this.href.length) ? 'a' : 'div',
30412 cls: 'roo-brick-paragraph',
30418 if(this.href.length){
30419 cfg.href = this.href;
30422 var cn = cfg.cn[0].cn;
30424 if(this.title.length){
30427 cls: 'roo-brick-title',
30432 if(this.html.length){
30435 cls: 'roo-brick-text',
30440 if(this.bgimage.length){
30443 cls: 'roo-brick-image-view',
30451 initEvents: function()
30453 if(this.title.length || this.html.length){
30454 this.el.on('mouseenter' ,this.enter, this);
30455 this.el.on('mouseleave', this.leave, this);
30459 Roo.EventManager.onWindowResize(this.resize, this);
30464 resize : function()
30466 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30468 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30469 // paragraph.setHeight(paragraph.getWidth());
30471 if(this.bgimage.length){
30472 var image = this.el.select('.roo-brick-image-view', true).first();
30473 image.setWidth(paragraph.getWidth());
30474 image.setHeight(paragraph.getWidth());
30479 enter: function(e, el)
30481 e.preventDefault();
30483 if(this.bgimage.length){
30484 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30485 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30489 leave: function(e, el)
30491 e.preventDefault();
30493 if(this.bgimage.length){
30494 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30495 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30505 * Ext JS Library 1.1.1
30506 * Copyright(c) 2006-2007, Ext JS, LLC.
30508 * Originally Released Under LGPL - original licence link has changed is not relivant.
30511 * <script type="text/javascript">
30516 * @class Roo.bootstrap.SplitBar
30517 * @extends Roo.util.Observable
30518 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30522 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30523 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30524 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30525 split.minSize = 100;
30526 split.maxSize = 600;
30527 split.animate = true;
30528 split.on('moved', splitterMoved);
30531 * Create a new SplitBar
30532 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30533 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30534 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30535 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30536 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30537 position of the SplitBar).
30539 Roo.bootstrap.SplitBar = function(cfg){
30544 // dragElement : elm
30545 // resizingElement: el,
30547 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30548 // placement : Roo.bootstrap.SplitBar.LEFT ,
30549 // existingProxy ???
30552 this.el = Roo.get(cfg.dragElement, true);
30553 this.el.dom.unselectable = "on";
30555 this.resizingEl = Roo.get(cfg.resizingElement, true);
30559 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30560 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30563 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30566 * The minimum size of the resizing element. (Defaults to 0)
30572 * The maximum size of the resizing element. (Defaults to 2000)
30575 this.maxSize = 2000;
30578 * Whether to animate the transition to the new size
30581 this.animate = false;
30584 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30587 this.useShim = false;
30592 if(!cfg.existingProxy){
30594 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30596 this.proxy = Roo.get(cfg.existingProxy).dom;
30599 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30602 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30605 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30608 this.dragSpecs = {};
30611 * @private The adapter to use to positon and resize elements
30613 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30614 this.adapter.init(this);
30616 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30618 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30619 this.el.addClass("roo-splitbar-h");
30622 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30623 this.el.addClass("roo-splitbar-v");
30629 * Fires when the splitter is moved (alias for {@link #event-moved})
30630 * @param {Roo.bootstrap.SplitBar} this
30631 * @param {Number} newSize the new width or height
30636 * Fires when the splitter is moved
30637 * @param {Roo.bootstrap.SplitBar} this
30638 * @param {Number} newSize the new width or height
30642 * @event beforeresize
30643 * Fires before the splitter is dragged
30644 * @param {Roo.bootstrap.SplitBar} this
30646 "beforeresize" : true,
30648 "beforeapply" : true
30651 Roo.util.Observable.call(this);
30654 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30655 onStartProxyDrag : function(x, y){
30656 this.fireEvent("beforeresize", this);
30658 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30660 o.enableDisplayMode("block");
30661 // all splitbars share the same overlay
30662 Roo.bootstrap.SplitBar.prototype.overlay = o;
30664 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30665 this.overlay.show();
30666 Roo.get(this.proxy).setDisplayed("block");
30667 var size = this.adapter.getElementSize(this);
30668 this.activeMinSize = this.getMinimumSize();;
30669 this.activeMaxSize = this.getMaximumSize();;
30670 var c1 = size - this.activeMinSize;
30671 var c2 = Math.max(this.activeMaxSize - size, 0);
30672 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30673 this.dd.resetConstraints();
30674 this.dd.setXConstraint(
30675 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30676 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30678 this.dd.setYConstraint(0, 0);
30680 this.dd.resetConstraints();
30681 this.dd.setXConstraint(0, 0);
30682 this.dd.setYConstraint(
30683 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30684 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30687 this.dragSpecs.startSize = size;
30688 this.dragSpecs.startPoint = [x, y];
30689 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30693 * @private Called after the drag operation by the DDProxy
30695 onEndProxyDrag : function(e){
30696 Roo.get(this.proxy).setDisplayed(false);
30697 var endPoint = Roo.lib.Event.getXY(e);
30699 this.overlay.hide();
30702 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30703 newSize = this.dragSpecs.startSize +
30704 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30705 endPoint[0] - this.dragSpecs.startPoint[0] :
30706 this.dragSpecs.startPoint[0] - endPoint[0]
30709 newSize = this.dragSpecs.startSize +
30710 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30711 endPoint[1] - this.dragSpecs.startPoint[1] :
30712 this.dragSpecs.startPoint[1] - endPoint[1]
30715 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30716 if(newSize != this.dragSpecs.startSize){
30717 if(this.fireEvent('beforeapply', this, newSize) !== false){
30718 this.adapter.setElementSize(this, newSize);
30719 this.fireEvent("moved", this, newSize);
30720 this.fireEvent("resize", this, newSize);
30726 * Get the adapter this SplitBar uses
30727 * @return The adapter object
30729 getAdapter : function(){
30730 return this.adapter;
30734 * Set the adapter this SplitBar uses
30735 * @param {Object} adapter A SplitBar adapter object
30737 setAdapter : function(adapter){
30738 this.adapter = adapter;
30739 this.adapter.init(this);
30743 * Gets the minimum size for the resizing element
30744 * @return {Number} The minimum size
30746 getMinimumSize : function(){
30747 return this.minSize;
30751 * Sets the minimum size for the resizing element
30752 * @param {Number} minSize The minimum size
30754 setMinimumSize : function(minSize){
30755 this.minSize = minSize;
30759 * Gets the maximum size for the resizing element
30760 * @return {Number} The maximum size
30762 getMaximumSize : function(){
30763 return this.maxSize;
30767 * Sets the maximum size for the resizing element
30768 * @param {Number} maxSize The maximum size
30770 setMaximumSize : function(maxSize){
30771 this.maxSize = maxSize;
30775 * Sets the initialize size for the resizing element
30776 * @param {Number} size The initial size
30778 setCurrentSize : function(size){
30779 var oldAnimate = this.animate;
30780 this.animate = false;
30781 this.adapter.setElementSize(this, size);
30782 this.animate = oldAnimate;
30786 * Destroy this splitbar.
30787 * @param {Boolean} removeEl True to remove the element
30789 destroy : function(removeEl){
30791 this.shim.remove();
30794 this.proxy.parentNode.removeChild(this.proxy);
30802 * @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.
30804 Roo.bootstrap.SplitBar.createProxy = function(dir){
30805 var proxy = new Roo.Element(document.createElement("div"));
30806 proxy.unselectable();
30807 var cls = 'roo-splitbar-proxy';
30808 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30809 document.body.appendChild(proxy.dom);
30814 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30815 * Default Adapter. It assumes the splitter and resizing element are not positioned
30816 * elements and only gets/sets the width of the element. Generally used for table based layouts.
30818 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30821 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30822 // do nothing for now
30823 init : function(s){
30827 * Called before drag operations to get the current size of the resizing element.
30828 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30830 getElementSize : function(s){
30831 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30832 return s.resizingEl.getWidth();
30834 return s.resizingEl.getHeight();
30839 * Called after drag operations to set the size of the resizing element.
30840 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30841 * @param {Number} newSize The new size to set
30842 * @param {Function} onComplete A function to be invoked when resizing is complete
30844 setElementSize : function(s, newSize, onComplete){
30845 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30847 s.resizingEl.setWidth(newSize);
30849 onComplete(s, newSize);
30852 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30857 s.resizingEl.setHeight(newSize);
30859 onComplete(s, newSize);
30862 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30869 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30870 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30871 * Adapter that moves the splitter element to align with the resized sizing element.
30872 * Used with an absolute positioned SplitBar.
30873 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30874 * document.body, make sure you assign an id to the body element.
30876 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30877 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30878 this.container = Roo.get(container);
30881 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30882 init : function(s){
30883 this.basic.init(s);
30886 getElementSize : function(s){
30887 return this.basic.getElementSize(s);
30890 setElementSize : function(s, newSize, onComplete){
30891 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30894 moveSplitter : function(s){
30895 var yes = Roo.bootstrap.SplitBar;
30896 switch(s.placement){
30898 s.el.setX(s.resizingEl.getRight());
30901 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30904 s.el.setY(s.resizingEl.getBottom());
30907 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30914 * Orientation constant - Create a vertical SplitBar
30918 Roo.bootstrap.SplitBar.VERTICAL = 1;
30921 * Orientation constant - Create a horizontal SplitBar
30925 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30928 * Placement constant - The resizing element is to the left of the splitter element
30932 Roo.bootstrap.SplitBar.LEFT = 1;
30935 * Placement constant - The resizing element is to the right of the splitter element
30939 Roo.bootstrap.SplitBar.RIGHT = 2;
30942 * Placement constant - The resizing element is positioned above the splitter element
30946 Roo.bootstrap.SplitBar.TOP = 3;
30949 * Placement constant - The resizing element is positioned under splitter element
30953 Roo.bootstrap.SplitBar.BOTTOM = 4;
30954 Roo.namespace("Roo.bootstrap.layout");/*
30956 * Ext JS Library 1.1.1
30957 * Copyright(c) 2006-2007, Ext JS, LLC.
30959 * Originally Released Under LGPL - original licence link has changed is not relivant.
30962 * <script type="text/javascript">
30966 * @class Roo.bootstrap.layout.Manager
30967 * @extends Roo.bootstrap.Component
30968 * Base class for layout managers.
30970 Roo.bootstrap.layout.Manager = function(config)
30972 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
30973 this.el = Roo.get(config.el);
30974 // ie scrollbar fix
30975 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30976 document.body.scroll = "no";
30977 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30978 this.el.position('relative');
30981 this.id = this.el.id;
30982 this.el.addClass("roo-layout-container");
30983 /** false to disable window resize monitoring @type Boolean */
30984 this.monitorWindowResize = true;
30989 * Fires when a layout is performed.
30990 * @param {Roo.LayoutManager} this
30994 * @event regionresized
30995 * Fires when the user resizes a region.
30996 * @param {Roo.LayoutRegion} region The resized region
30997 * @param {Number} newSize The new size (width for east/west, height for north/south)
30999 "regionresized" : true,
31001 * @event regioncollapsed
31002 * Fires when a region is collapsed.
31003 * @param {Roo.LayoutRegion} region The collapsed region
31005 "regioncollapsed" : true,
31007 * @event regionexpanded
31008 * Fires when a region is expanded.
31009 * @param {Roo.LayoutRegion} region The expanded region
31011 "regionexpanded" : true
31013 this.updating = false;
31014 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31017 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31022 monitorWindowResize : true,
31028 * Returns true if this layout is currently being updated
31029 * @return {Boolean}
31031 isUpdating : function(){
31032 return this.updating;
31036 * Suspend the LayoutManager from doing auto-layouts while
31037 * making multiple add or remove calls
31039 beginUpdate : function(){
31040 this.updating = true;
31044 * Restore auto-layouts and optionally disable the manager from performing a layout
31045 * @param {Boolean} noLayout true to disable a layout update
31047 endUpdate : function(noLayout){
31048 this.updating = false;
31054 layout: function(){
31058 onRegionResized : function(region, newSize){
31059 this.fireEvent("regionresized", region, newSize);
31063 onRegionCollapsed : function(region){
31064 this.fireEvent("regioncollapsed", region);
31067 onRegionExpanded : function(region){
31068 this.fireEvent("regionexpanded", region);
31072 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31073 * performs box-model adjustments.
31074 * @return {Object} The size as an object {width: (the width), height: (the height)}
31076 getViewSize : function()
31079 if(this.el.dom != document.body){
31080 size = this.el.getSize();
31082 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31084 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31085 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31090 * Returns the Element this layout is bound to.
31091 * @return {Roo.Element}
31093 getEl : function(){
31098 * Returns the specified region.
31099 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31100 * @return {Roo.LayoutRegion}
31102 getRegion : function(target){
31103 return this.regions[target.toLowerCase()];
31106 onWindowResize : function(){
31107 if(this.monitorWindowResize){
31113 * Ext JS Library 1.1.1
31114 * Copyright(c) 2006-2007, Ext JS, LLC.
31116 * Originally Released Under LGPL - original licence link has changed is not relivant.
31119 * <script type="text/javascript">
31122 * @class Roo.bootstrap.layout.Border
31123 * @extends Roo.bootstrap.layout.Manager
31124 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31125 * please see: examples/bootstrap/nested.html<br><br>
31127 <b>The container the layout is rendered into can be either the body element or any other element.
31128 If it is not the body element, the container needs to either be an absolute positioned element,
31129 or you will need to add "position:relative" to the css of the container. You will also need to specify
31130 the container size if it is not the body element.</b>
31133 * Create a new Border
31134 * @param {Object} config Configuration options
31136 Roo.bootstrap.layout.Border = function(config){
31137 config = config || {};
31138 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31142 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31143 if(config[region]){
31144 config[region].region = region;
31145 this.addRegion(config[region]);
31151 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31153 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31155 * Creates and adds a new region if it doesn't already exist.
31156 * @param {String} target The target region key (north, south, east, west or center).
31157 * @param {Object} config The regions config object
31158 * @return {BorderLayoutRegion} The new region
31160 addRegion : function(config)
31162 if(!this.regions[config.region]){
31163 var r = this.factory(config);
31164 this.bindRegion(r);
31166 return this.regions[config.region];
31170 bindRegion : function(r){
31171 this.regions[r.config.region] = r;
31173 r.on("visibilitychange", this.layout, this);
31174 r.on("paneladded", this.layout, this);
31175 r.on("panelremoved", this.layout, this);
31176 r.on("invalidated", this.layout, this);
31177 r.on("resized", this.onRegionResized, this);
31178 r.on("collapsed", this.onRegionCollapsed, this);
31179 r.on("expanded", this.onRegionExpanded, this);
31183 * Performs a layout update.
31185 layout : function()
31187 if(this.updating) {
31190 var size = this.getViewSize();
31191 var w = size.width;
31192 var h = size.height;
31197 //var x = 0, y = 0;
31199 var rs = this.regions;
31200 var north = rs["north"];
31201 var south = rs["south"];
31202 var west = rs["west"];
31203 var east = rs["east"];
31204 var center = rs["center"];
31205 //if(this.hideOnLayout){ // not supported anymore
31206 //c.el.setStyle("display", "none");
31208 if(north && north.isVisible()){
31209 var b = north.getBox();
31210 var m = north.getMargins();
31211 b.width = w - (m.left+m.right);
31214 centerY = b.height + b.y + m.bottom;
31215 centerH -= centerY;
31216 north.updateBox(this.safeBox(b));
31218 if(south && south.isVisible()){
31219 var b = south.getBox();
31220 var m = south.getMargins();
31221 b.width = w - (m.left+m.right);
31223 var totalHeight = (b.height + m.top + m.bottom);
31224 b.y = h - totalHeight + m.top;
31225 centerH -= totalHeight;
31226 south.updateBox(this.safeBox(b));
31228 if(west && west.isVisible()){
31229 var b = west.getBox();
31230 var m = west.getMargins();
31231 b.height = centerH - (m.top+m.bottom);
31233 b.y = centerY + m.top;
31234 var totalWidth = (b.width + m.left + m.right);
31235 centerX += totalWidth;
31236 centerW -= totalWidth;
31237 west.updateBox(this.safeBox(b));
31239 if(east && east.isVisible()){
31240 var b = east.getBox();
31241 var m = east.getMargins();
31242 b.height = centerH - (m.top+m.bottom);
31243 var totalWidth = (b.width + m.left + m.right);
31244 b.x = w - totalWidth + m.left;
31245 b.y = centerY + m.top;
31246 centerW -= totalWidth;
31247 east.updateBox(this.safeBox(b));
31250 var m = center.getMargins();
31252 x: centerX + m.left,
31253 y: centerY + m.top,
31254 width: centerW - (m.left+m.right),
31255 height: centerH - (m.top+m.bottom)
31257 //if(this.hideOnLayout){
31258 //center.el.setStyle("display", "block");
31260 center.updateBox(this.safeBox(centerBox));
31263 this.fireEvent("layout", this);
31267 safeBox : function(box){
31268 box.width = Math.max(0, box.width);
31269 box.height = Math.max(0, box.height);
31274 * Adds a ContentPanel (or subclass) to this layout.
31275 * @param {String} target The target region key (north, south, east, west or center).
31276 * @param {Roo.ContentPanel} panel The panel to add
31277 * @return {Roo.ContentPanel} The added panel
31279 add : function(target, panel){
31281 target = target.toLowerCase();
31282 return this.regions[target].add(panel);
31286 * Remove a ContentPanel (or subclass) to this layout.
31287 * @param {String} target The target region key (north, south, east, west or center).
31288 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31289 * @return {Roo.ContentPanel} The removed panel
31291 remove : function(target, panel){
31292 target = target.toLowerCase();
31293 return this.regions[target].remove(panel);
31297 * Searches all regions for a panel with the specified id
31298 * @param {String} panelId
31299 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31301 findPanel : function(panelId){
31302 var rs = this.regions;
31303 for(var target in rs){
31304 if(typeof rs[target] != "function"){
31305 var p = rs[target].getPanel(panelId);
31315 * Searches all regions for a panel with the specified id and activates (shows) it.
31316 * @param {String/ContentPanel} panelId The panels id or the panel itself
31317 * @return {Roo.ContentPanel} The shown panel or null
31319 showPanel : function(panelId) {
31320 var rs = this.regions;
31321 for(var target in rs){
31322 var r = rs[target];
31323 if(typeof r != "function"){
31324 if(r.hasPanel(panelId)){
31325 return r.showPanel(panelId);
31333 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31334 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31337 restoreState : function(provider){
31339 provider = Roo.state.Manager;
31341 var sm = new Roo.LayoutStateManager();
31342 sm.init(this, provider);
31348 * Adds a xtype elements to the layout.
31352 xtype : 'ContentPanel',
31359 xtype : 'NestedLayoutPanel',
31365 items : [ ... list of content panels or nested layout panels.. ]
31369 * @param {Object} cfg Xtype definition of item to add.
31371 addxtype : function(cfg)
31373 // basically accepts a pannel...
31374 // can accept a layout region..!?!?
31375 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31378 // theory? children can only be panels??
31380 //if (!cfg.xtype.match(/Panel$/)) {
31385 if (typeof(cfg.region) == 'undefined') {
31386 Roo.log("Failed to add Panel, region was not set");
31390 var region = cfg.region;
31396 xitems = cfg.items;
31403 case 'Content': // ContentPanel (el, cfg)
31404 case 'Scroll': // ContentPanel (el, cfg)
31406 cfg.autoCreate = true;
31407 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31409 // var el = this.el.createChild();
31410 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31413 this.add(region, ret);
31417 case 'TreePanel': // our new panel!
31418 cfg.el = this.el.createChild();
31419 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31420 this.add(region, ret);
31425 // create a new Layout (which is a Border Layout...
31427 var clayout = cfg.layout;
31428 clayout.el = this.el.createChild();
31429 clayout.items = clayout.items || [];
31433 // replace this exitems with the clayout ones..
31434 xitems = clayout.items;
31436 // force background off if it's in center...
31437 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31438 cfg.background = false;
31440 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31443 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31444 //console.log('adding nested layout panel ' + cfg.toSource());
31445 this.add(region, ret);
31446 nb = {}; /// find first...
31451 // needs grid and region
31453 //var el = this.getRegion(region).el.createChild();
31454 var el = this.el.createChild();
31455 // create the grid first...
31457 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31459 if (region == 'center' && this.active ) {
31460 cfg.background = false;
31462 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31464 this.add(region, ret);
31465 if (cfg.background) {
31466 ret.on('activate', function(gp) {
31467 if (!gp.grid.rendered) {
31477 case 'Border': // it can get called on it'self...
31487 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31489 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31490 this.add(region, ret);
31494 throw "Can not add '" + cfg.xtype + "' to Border";
31498 // GridPanel (grid, cfg)
31501 this.beginUpdate();
31505 Roo.each(xitems, function(i) {
31506 region = nb && i.region ? i.region : false;
31508 var add = ret.addxtype(i);
31511 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31512 if (!i.background) {
31513 abn[region] = nb[region] ;
31520 // make the last non-background panel active..
31521 //if (nb) { Roo.log(abn); }
31524 for(var r in abn) {
31525 region = this.getRegion(r);
31527 // tried using nb[r], but it does not work..
31529 region.showPanel(abn[r]);
31540 factory : function(cfg)
31543 var validRegions = Roo.bootstrap.layout.Border.regions;
31545 var target = cfg.region;
31548 var r = Roo.bootstrap.layout;
31552 return new r.North(cfg);
31554 return new r.South(cfg);
31556 return new r.East(cfg);
31558 return new r.West(cfg);
31560 return new r.Center(cfg);
31562 throw 'Layout region "'+target+'" not supported.';
31569 * Ext JS Library 1.1.1
31570 * Copyright(c) 2006-2007, Ext JS, LLC.
31572 * Originally Released Under LGPL - original licence link has changed is not relivant.
31575 * <script type="text/javascript">
31579 * @class Roo.bootstrap.layout.Basic
31580 * @extends Roo.util.Observable
31581 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31582 * and does not have a titlebar, tabs or any other features. All it does is size and position
31583 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31584 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31585 * @cfg {string} region the region that it inhabits..
31586 * @cfg {bool} skipConfig skip config?
31590 Roo.bootstrap.layout.Basic = function(config){
31592 this.mgr = config.mgr;
31594 this.position = config.region;
31596 var skipConfig = config.skipConfig;
31600 * @scope Roo.BasicLayoutRegion
31604 * @event beforeremove
31605 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31606 * @param {Roo.LayoutRegion} this
31607 * @param {Roo.ContentPanel} panel The panel
31608 * @param {Object} e The cancel event object
31610 "beforeremove" : true,
31612 * @event invalidated
31613 * Fires when the layout for this region is changed.
31614 * @param {Roo.LayoutRegion} this
31616 "invalidated" : true,
31618 * @event visibilitychange
31619 * Fires when this region is shown or hidden
31620 * @param {Roo.LayoutRegion} this
31621 * @param {Boolean} visibility true or false
31623 "visibilitychange" : true,
31625 * @event paneladded
31626 * Fires when a panel is added.
31627 * @param {Roo.LayoutRegion} this
31628 * @param {Roo.ContentPanel} panel The panel
31630 "paneladded" : true,
31632 * @event panelremoved
31633 * Fires when a panel is removed.
31634 * @param {Roo.LayoutRegion} this
31635 * @param {Roo.ContentPanel} panel The panel
31637 "panelremoved" : true,
31639 * @event beforecollapse
31640 * Fires when this region before collapse.
31641 * @param {Roo.LayoutRegion} this
31643 "beforecollapse" : true,
31646 * Fires when this region is collapsed.
31647 * @param {Roo.LayoutRegion} this
31649 "collapsed" : true,
31652 * Fires when this region is expanded.
31653 * @param {Roo.LayoutRegion} this
31658 * Fires when this region is slid into view.
31659 * @param {Roo.LayoutRegion} this
31661 "slideshow" : true,
31664 * Fires when this region slides out of view.
31665 * @param {Roo.LayoutRegion} this
31667 "slidehide" : true,
31669 * @event panelactivated
31670 * Fires when a panel is activated.
31671 * @param {Roo.LayoutRegion} this
31672 * @param {Roo.ContentPanel} panel The activated panel
31674 "panelactivated" : true,
31677 * Fires when the user resizes this region.
31678 * @param {Roo.LayoutRegion} this
31679 * @param {Number} newSize The new size (width for east/west, height for north/south)
31683 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31684 this.panels = new Roo.util.MixedCollection();
31685 this.panels.getKey = this.getPanelId.createDelegate(this);
31687 this.activePanel = null;
31688 // ensure listeners are added...
31690 if (config.listeners || config.events) {
31691 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31692 listeners : config.listeners || {},
31693 events : config.events || {}
31697 if(skipConfig !== true){
31698 this.applyConfig(config);
31702 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31704 getPanelId : function(p){
31708 applyConfig : function(config){
31709 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31710 this.config = config;
31715 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31716 * the width, for horizontal (north, south) the height.
31717 * @param {Number} newSize The new width or height
31719 resizeTo : function(newSize){
31720 var el = this.el ? this.el :
31721 (this.activePanel ? this.activePanel.getEl() : null);
31723 switch(this.position){
31726 el.setWidth(newSize);
31727 this.fireEvent("resized", this, newSize);
31731 el.setHeight(newSize);
31732 this.fireEvent("resized", this, newSize);
31738 getBox : function(){
31739 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31742 getMargins : function(){
31743 return this.margins;
31746 updateBox : function(box){
31748 var el = this.activePanel.getEl();
31749 el.dom.style.left = box.x + "px";
31750 el.dom.style.top = box.y + "px";
31751 this.activePanel.setSize(box.width, box.height);
31755 * Returns the container element for this region.
31756 * @return {Roo.Element}
31758 getEl : function(){
31759 return this.activePanel;
31763 * Returns true if this region is currently visible.
31764 * @return {Boolean}
31766 isVisible : function(){
31767 return this.activePanel ? true : false;
31770 setActivePanel : function(panel){
31771 panel = this.getPanel(panel);
31772 if(this.activePanel && this.activePanel != panel){
31773 this.activePanel.setActiveState(false);
31774 this.activePanel.getEl().setLeftTop(-10000,-10000);
31776 this.activePanel = panel;
31777 panel.setActiveState(true);
31779 panel.setSize(this.box.width, this.box.height);
31781 this.fireEvent("panelactivated", this, panel);
31782 this.fireEvent("invalidated");
31786 * Show the specified panel.
31787 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31788 * @return {Roo.ContentPanel} The shown panel or null
31790 showPanel : function(panel){
31791 panel = this.getPanel(panel);
31793 this.setActivePanel(panel);
31799 * Get the active panel for this region.
31800 * @return {Roo.ContentPanel} The active panel or null
31802 getActivePanel : function(){
31803 return this.activePanel;
31807 * Add the passed ContentPanel(s)
31808 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31809 * @return {Roo.ContentPanel} The panel added (if only one was added)
31811 add : function(panel){
31812 if(arguments.length > 1){
31813 for(var i = 0, len = arguments.length; i < len; i++) {
31814 this.add(arguments[i]);
31818 if(this.hasPanel(panel)){
31819 this.showPanel(panel);
31822 var el = panel.getEl();
31823 if(el.dom.parentNode != this.mgr.el.dom){
31824 this.mgr.el.dom.appendChild(el.dom);
31826 if(panel.setRegion){
31827 panel.setRegion(this);
31829 this.panels.add(panel);
31830 el.setStyle("position", "absolute");
31831 if(!panel.background){
31832 this.setActivePanel(panel);
31833 if(this.config.initialSize && this.panels.getCount()==1){
31834 this.resizeTo(this.config.initialSize);
31837 this.fireEvent("paneladded", this, panel);
31842 * Returns true if the panel is in this region.
31843 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31844 * @return {Boolean}
31846 hasPanel : function(panel){
31847 if(typeof panel == "object"){ // must be panel obj
31848 panel = panel.getId();
31850 return this.getPanel(panel) ? true : false;
31854 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31855 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31856 * @param {Boolean} preservePanel Overrides the config preservePanel option
31857 * @return {Roo.ContentPanel} The panel that was removed
31859 remove : function(panel, preservePanel){
31860 panel = this.getPanel(panel);
31865 this.fireEvent("beforeremove", this, panel, e);
31866 if(e.cancel === true){
31869 var panelId = panel.getId();
31870 this.panels.removeKey(panelId);
31875 * Returns the panel specified or null if it's not in this region.
31876 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31877 * @return {Roo.ContentPanel}
31879 getPanel : function(id){
31880 if(typeof id == "object"){ // must be panel obj
31883 return this.panels.get(id);
31887 * Returns this regions position (north/south/east/west/center).
31890 getPosition: function(){
31891 return this.position;
31895 * Ext JS Library 1.1.1
31896 * Copyright(c) 2006-2007, Ext JS, LLC.
31898 * Originally Released Under LGPL - original licence link has changed is not relivant.
31901 * <script type="text/javascript">
31905 * @class Roo.bootstrap.layout.Region
31906 * @extends Roo.bootstrap.layout.Basic
31907 * This class represents a region in a layout manager.
31909 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31910 * @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})
31911 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
31912 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
31913 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
31914 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
31915 * @cfg {String} title The title for the region (overrides panel titles)
31916 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
31917 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31918 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
31919 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31920 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
31921 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31922 * the space available, similar to FireFox 1.5 tabs (defaults to false)
31923 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
31924 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
31925 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
31927 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
31928 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
31929 * @cfg {Boolean} disableTabTips True to disable tab tooltips
31930 * @cfg {Number} width For East/West panels
31931 * @cfg {Number} height For North/South panels
31932 * @cfg {Boolean} split To show the splitter
31933 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
31935 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31936 * @cfg {string} region the region that it inhabits..
31939 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
31940 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
31942 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
31943 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
31944 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
31946 Roo.bootstrap.layout.Region = function(config)
31949 var mgr = config.mgr;
31950 var pos = config.region;
31951 config.skipConfig = true;
31952 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
31953 var dh = Roo.DomHelper;
31954 /** This region's container element
31955 * @type Roo.Element */
31956 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
31957 /** This region's title element
31958 * @type Roo.Element */
31960 this.titleEl = dh.append(this.el.dom,
31963 unselectable: "on",
31964 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
31966 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
31967 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
31970 this.titleEl.enableDisplayMode();
31971 /** This region's title text element
31972 * @type HTMLElement */
31973 this.titleTextEl = this.titleEl.dom.firstChild;
31974 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31976 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
31977 this.closeBtn.enableDisplayMode();
31978 this.closeBtn.on("click", this.closeClicked, this);
31979 this.closeBtn.hide();
31981 this.createBody(config);
31982 this.visible = true;
31983 this.collapsed = false;
31985 if(config.hideWhenEmpty){
31987 this.on("paneladded", this.validateVisibility, this);
31988 this.on("panelremoved", this.validateVisibility, this);
31990 this.applyConfig(config);
31993 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
31997 createBody : function(){
31998 /** This region's body element
31999 * @type Roo.Element */
32000 this.bodyEl = this.el.createChild({
32002 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32006 applyConfig : function(c)
32009 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32010 var dh = Roo.DomHelper;
32011 if(c.titlebar !== false){
32012 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32013 this.collapseBtn.on("click", this.collapse, this);
32014 this.collapseBtn.enableDisplayMode();
32016 if(c.showPin === true || this.showPin){
32017 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32018 this.stickBtn.enableDisplayMode();
32019 this.stickBtn.on("click", this.expand, this);
32020 this.stickBtn.hide();
32025 /** This region's collapsed element
32026 * @type Roo.Element */
32029 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32030 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32033 if(c.floatable !== false){
32034 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32035 this.collapsedEl.on("click", this.collapseClick, this);
32038 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32039 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32040 id: "message", unselectable: "on", style:{"float":"left"}});
32041 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32043 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32044 this.expandBtn.on("click", this.expand, this);
32048 if(this.collapseBtn){
32049 this.collapseBtn.setVisible(c.collapsible == true);
32052 this.cmargins = c.cmargins || this.cmargins ||
32053 (this.position == "west" || this.position == "east" ?
32054 {top: 0, left: 2, right:2, bottom: 0} :
32055 {top: 2, left: 0, right:0, bottom: 2});
32057 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32060 this.bottomTabs = c.tabPosition != "top";
32062 this.autoScroll = c.autoScroll || false;
32065 if(this.autoScroll){
32066 this.bodyEl.setStyle("overflow", "auto");
32068 this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32070 //if(c.titlebar !== false){
32071 if((!c.titlebar && !c.title) || c.titlebar === false){
32072 this.titleEl.hide();
32074 this.titleEl.show();
32076 this.titleTextEl.innerHTML = c.title;
32080 this.duration = c.duration || .30;
32081 this.slideDuration = c.slideDuration || .45;
32084 this.collapse(true);
32091 * Returns true if this region is currently visible.
32092 * @return {Boolean}
32094 isVisible : function(){
32095 return this.visible;
32099 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32100 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32102 //setCollapsedTitle : function(title){
32103 // title = title || " ";
32104 // if(this.collapsedTitleTextEl){
32105 // this.collapsedTitleTextEl.innerHTML = title;
32109 getBox : function(){
32111 // if(!this.collapsed){
32112 b = this.el.getBox(false, true);
32114 // b = this.collapsedEl.getBox(false, true);
32119 getMargins : function(){
32120 return this.margins;
32121 //return this.collapsed ? this.cmargins : this.margins;
32124 highlight : function(){
32125 this.el.addClass("x-layout-panel-dragover");
32128 unhighlight : function(){
32129 this.el.removeClass("x-layout-panel-dragover");
32132 updateBox : function(box)
32135 if(!this.collapsed){
32136 this.el.dom.style.left = box.x + "px";
32137 this.el.dom.style.top = box.y + "px";
32138 this.updateBody(box.width, box.height);
32140 this.collapsedEl.dom.style.left = box.x + "px";
32141 this.collapsedEl.dom.style.top = box.y + "px";
32142 this.collapsedEl.setSize(box.width, box.height);
32145 this.tabs.autoSizeTabs();
32149 updateBody : function(w, h)
32152 this.el.setWidth(w);
32153 w -= this.el.getBorderWidth("rl");
32154 if(this.config.adjustments){
32155 w += this.config.adjustments[0];
32159 this.el.setHeight(h);
32160 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32161 h -= this.el.getBorderWidth("tb");
32162 if(this.config.adjustments){
32163 h += this.config.adjustments[1];
32165 this.bodyEl.setHeight(h);
32167 h = this.tabs.syncHeight(h);
32170 if(this.panelSize){
32171 w = w !== null ? w : this.panelSize.width;
32172 h = h !== null ? h : this.panelSize.height;
32174 if(this.activePanel){
32175 var el = this.activePanel.getEl();
32176 w = w !== null ? w : el.getWidth();
32177 h = h !== null ? h : el.getHeight();
32178 this.panelSize = {width: w, height: h};
32179 this.activePanel.setSize(w, h);
32181 if(Roo.isIE && this.tabs){
32182 this.tabs.el.repaint();
32187 * Returns the container element for this region.
32188 * @return {Roo.Element}
32190 getEl : function(){
32195 * Hides this region.
32198 //if(!this.collapsed){
32199 this.el.dom.style.left = "-2000px";
32202 // this.collapsedEl.dom.style.left = "-2000px";
32203 // this.collapsedEl.hide();
32205 this.visible = false;
32206 this.fireEvent("visibilitychange", this, false);
32210 * Shows this region if it was previously hidden.
32213 //if(!this.collapsed){
32216 // this.collapsedEl.show();
32218 this.visible = true;
32219 this.fireEvent("visibilitychange", this, true);
32222 closeClicked : function(){
32223 if(this.activePanel){
32224 this.remove(this.activePanel);
32228 collapseClick : function(e){
32230 e.stopPropagation();
32233 e.stopPropagation();
32239 * Collapses this region.
32240 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32243 collapse : function(skipAnim, skipCheck = false){
32244 if(this.collapsed) {
32248 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32250 this.collapsed = true;
32252 this.split.el.hide();
32254 if(this.config.animate && skipAnim !== true){
32255 this.fireEvent("invalidated", this);
32256 this.animateCollapse();
32258 this.el.setLocation(-20000,-20000);
32260 this.collapsedEl.show();
32261 this.fireEvent("collapsed", this);
32262 this.fireEvent("invalidated", this);
32268 animateCollapse : function(){
32273 * Expands this region if it was previously collapsed.
32274 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32275 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32278 expand : function(e, skipAnim){
32280 e.stopPropagation();
32282 if(!this.collapsed || this.el.hasActiveFx()) {
32286 this.afterSlideIn();
32289 this.collapsed = false;
32290 if(this.config.animate && skipAnim !== true){
32291 this.animateExpand();
32295 this.split.el.show();
32297 this.collapsedEl.setLocation(-2000,-2000);
32298 this.collapsedEl.hide();
32299 this.fireEvent("invalidated", this);
32300 this.fireEvent("expanded", this);
32304 animateExpand : function(){
32308 initTabs : function()
32310 this.bodyEl.setStyle("overflow", "hidden");
32311 var ts = new Roo.bootstrap.panel.Tabs({
32312 el: this.bodyEl.dom,
32313 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32314 disableTooltips: this.config.disableTabTips,
32315 toolbar : this.config.toolbar
32318 if(this.config.hideTabs){
32319 ts.stripWrap.setDisplayed(false);
32322 ts.resizeTabs = this.config.resizeTabs === true;
32323 ts.minTabWidth = this.config.minTabWidth || 40;
32324 ts.maxTabWidth = this.config.maxTabWidth || 250;
32325 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32326 ts.monitorResize = false;
32327 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32328 ts.bodyEl.addClass('roo-layout-tabs-body');
32329 this.panels.each(this.initPanelAsTab, this);
32332 initPanelAsTab : function(panel){
32333 var ti = this.tabs.addTab(
32335 panel.getTitle(), null,
32336 this.config.closeOnTab && panel.isClosable()
32338 if(panel.tabTip !== undefined){
32339 ti.setTooltip(panel.tabTip);
32341 ti.on("activate", function(){
32342 this.setActivePanel(panel);
32345 if(this.config.closeOnTab){
32346 ti.on("beforeclose", function(t, e){
32348 this.remove(panel);
32354 updatePanelTitle : function(panel, title)
32356 if(this.activePanel == panel){
32357 this.updateTitle(title);
32360 var ti = this.tabs.getTab(panel.getEl().id);
32362 if(panel.tabTip !== undefined){
32363 ti.setTooltip(panel.tabTip);
32368 updateTitle : function(title){
32369 if(this.titleTextEl && !this.config.title){
32370 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32374 setActivePanel : function(panel)
32376 panel = this.getPanel(panel);
32377 if(this.activePanel && this.activePanel != panel){
32378 this.activePanel.setActiveState(false);
32380 this.activePanel = panel;
32381 panel.setActiveState(true);
32382 if(this.panelSize){
32383 panel.setSize(this.panelSize.width, this.panelSize.height);
32386 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32388 this.updateTitle(panel.getTitle());
32390 this.fireEvent("invalidated", this);
32392 this.fireEvent("panelactivated", this, panel);
32396 * Shows the specified panel.
32397 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32398 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32400 showPanel : function(panel)
32402 panel = this.getPanel(panel);
32405 var tab = this.tabs.getTab(panel.getEl().id);
32406 if(tab.isHidden()){
32407 this.tabs.unhideTab(tab.id);
32411 this.setActivePanel(panel);
32418 * Get the active panel for this region.
32419 * @return {Roo.ContentPanel} The active panel or null
32421 getActivePanel : function(){
32422 return this.activePanel;
32425 validateVisibility : function(){
32426 if(this.panels.getCount() < 1){
32427 this.updateTitle(" ");
32428 this.closeBtn.hide();
32431 if(!this.isVisible()){
32438 * Adds the passed ContentPanel(s) to this region.
32439 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32440 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32442 add : function(panel){
32443 if(arguments.length > 1){
32444 for(var i = 0, len = arguments.length; i < len; i++) {
32445 this.add(arguments[i]);
32449 if(this.hasPanel(panel)){
32450 this.showPanel(panel);
32453 panel.setRegion(this);
32454 this.panels.add(panel);
32455 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32456 this.bodyEl.dom.appendChild(panel.getEl().dom);
32457 if(panel.background !== true){
32458 this.setActivePanel(panel);
32460 this.fireEvent("paneladded", this, panel);
32466 this.initPanelAsTab(panel);
32470 if(panel.background !== true){
32471 this.tabs.activate(panel.getEl().id);
32473 this.fireEvent("paneladded", this, panel);
32478 * Hides the tab for the specified panel.
32479 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32481 hidePanel : function(panel){
32482 if(this.tabs && (panel = this.getPanel(panel))){
32483 this.tabs.hideTab(panel.getEl().id);
32488 * Unhides the tab for a previously hidden panel.
32489 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32491 unhidePanel : function(panel){
32492 if(this.tabs && (panel = this.getPanel(panel))){
32493 this.tabs.unhideTab(panel.getEl().id);
32497 clearPanels : function(){
32498 while(this.panels.getCount() > 0){
32499 this.remove(this.panels.first());
32504 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32505 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32506 * @param {Boolean} preservePanel Overrides the config preservePanel option
32507 * @return {Roo.ContentPanel} The panel that was removed
32509 remove : function(panel, preservePanel)
32511 panel = this.getPanel(panel);
32516 this.fireEvent("beforeremove", this, panel, e);
32517 if(e.cancel === true){
32520 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32521 var panelId = panel.getId();
32522 this.panels.removeKey(panelId);
32524 document.body.appendChild(panel.getEl().dom);
32527 this.tabs.removeTab(panel.getEl().id);
32528 }else if (!preservePanel){
32529 this.bodyEl.dom.removeChild(panel.getEl().dom);
32531 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32532 var p = this.panels.first();
32533 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32534 tempEl.appendChild(p.getEl().dom);
32535 this.bodyEl.update("");
32536 this.bodyEl.dom.appendChild(p.getEl().dom);
32538 this.updateTitle(p.getTitle());
32540 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32541 this.setActivePanel(p);
32543 panel.setRegion(null);
32544 if(this.activePanel == panel){
32545 this.activePanel = null;
32547 if(this.config.autoDestroy !== false && preservePanel !== true){
32548 try{panel.destroy();}catch(e){}
32550 this.fireEvent("panelremoved", this, panel);
32555 * Returns the TabPanel component used by this region
32556 * @return {Roo.TabPanel}
32558 getTabs : function(){
32562 createTool : function(parentEl, className){
32563 var btn = Roo.DomHelper.append(parentEl, {
32565 cls: "x-layout-tools-button",
32568 cls: "roo-layout-tools-button-inner " + className,
32572 btn.addClassOnOver("roo-layout-tools-button-over");
32577 * Ext JS Library 1.1.1
32578 * Copyright(c) 2006-2007, Ext JS, LLC.
32580 * Originally Released Under LGPL - original licence link has changed is not relivant.
32583 * <script type="text/javascript">
32589 * @class Roo.SplitLayoutRegion
32590 * @extends Roo.LayoutRegion
32591 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32593 Roo.bootstrap.layout.Split = function(config){
32594 this.cursor = config.cursor;
32595 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32598 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32600 splitTip : "Drag to resize.",
32601 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32602 useSplitTips : false,
32604 applyConfig : function(config){
32605 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32611 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, {
32613 id: this.el.id + "-split",
32614 cls: "roo-layout-split roo-layout-split-"+this.position,
32617 /** The SplitBar for this region
32618 * @type Roo.SplitBar */
32619 // does not exist yet...
32620 Roo.log([this.position, this.orientation]);
32622 this.split = new Roo.bootstrap.SplitBar({
32623 dragElement : splitEl,
32624 resizingElement: this.el,
32625 orientation : this.orientation
32628 this.split.on("moved", this.onSplitMove, this);
32629 this.split.useShim = config.useShim === true;
32630 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32631 if(this.useSplitTips){
32632 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32634 //if(config.collapsible){
32635 // this.split.el.on("dblclick", this.collapse, this);
32638 if(typeof config.minSize != "undefined"){
32639 this.split.minSize = config.minSize;
32641 if(typeof config.maxSize != "undefined"){
32642 this.split.maxSize = config.maxSize;
32644 if(config.hideWhenEmpty || config.hidden || config.collapsed){
32645 this.hideSplitter();
32650 getHMaxSize : function(){
32651 var cmax = this.config.maxSize || 10000;
32652 var center = this.mgr.getRegion("center");
32653 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32656 getVMaxSize : function(){
32657 var cmax = this.config.maxSize || 10000;
32658 var center = this.mgr.getRegion("center");
32659 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32662 onSplitMove : function(split, newSize){
32663 this.fireEvent("resized", this, newSize);
32667 * Returns the {@link Roo.SplitBar} for this region.
32668 * @return {Roo.SplitBar}
32670 getSplitBar : function(){
32675 this.hideSplitter();
32676 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32679 hideSplitter : function(){
32681 this.split.el.setLocation(-2000,-2000);
32682 this.split.el.hide();
32688 this.split.el.show();
32690 Roo.bootstrap.layout.Split.superclass.show.call(this);
32693 beforeSlide: function(){
32694 if(Roo.isGecko){// firefox overflow auto bug workaround
32695 this.bodyEl.clip();
32697 this.tabs.bodyEl.clip();
32699 if(this.activePanel){
32700 this.activePanel.getEl().clip();
32702 if(this.activePanel.beforeSlide){
32703 this.activePanel.beforeSlide();
32709 afterSlide : function(){
32710 if(Roo.isGecko){// firefox overflow auto bug workaround
32711 this.bodyEl.unclip();
32713 this.tabs.bodyEl.unclip();
32715 if(this.activePanel){
32716 this.activePanel.getEl().unclip();
32717 if(this.activePanel.afterSlide){
32718 this.activePanel.afterSlide();
32724 initAutoHide : function(){
32725 if(this.autoHide !== false){
32726 if(!this.autoHideHd){
32727 var st = new Roo.util.DelayedTask(this.slideIn, this);
32728 this.autoHideHd = {
32729 "mouseout": function(e){
32730 if(!e.within(this.el, true)){
32734 "mouseover" : function(e){
32740 this.el.on(this.autoHideHd);
32744 clearAutoHide : function(){
32745 if(this.autoHide !== false){
32746 this.el.un("mouseout", this.autoHideHd.mouseout);
32747 this.el.un("mouseover", this.autoHideHd.mouseover);
32751 clearMonitor : function(){
32752 Roo.get(document).un("click", this.slideInIf, this);
32755 // these names are backwards but not changed for compat
32756 slideOut : function(){
32757 if(this.isSlid || this.el.hasActiveFx()){
32760 this.isSlid = true;
32761 if(this.collapseBtn){
32762 this.collapseBtn.hide();
32764 this.closeBtnState = this.closeBtn.getStyle('display');
32765 this.closeBtn.hide();
32767 this.stickBtn.show();
32770 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32771 this.beforeSlide();
32772 this.el.setStyle("z-index", 10001);
32773 this.el.slideIn(this.getSlideAnchor(), {
32774 callback: function(){
32776 this.initAutoHide();
32777 Roo.get(document).on("click", this.slideInIf, this);
32778 this.fireEvent("slideshow", this);
32785 afterSlideIn : function(){
32786 this.clearAutoHide();
32787 this.isSlid = false;
32788 this.clearMonitor();
32789 this.el.setStyle("z-index", "");
32790 if(this.collapseBtn){
32791 this.collapseBtn.show();
32793 this.closeBtn.setStyle('display', this.closeBtnState);
32795 this.stickBtn.hide();
32797 this.fireEvent("slidehide", this);
32800 slideIn : function(cb){
32801 if(!this.isSlid || this.el.hasActiveFx()){
32805 this.isSlid = false;
32806 this.beforeSlide();
32807 this.el.slideOut(this.getSlideAnchor(), {
32808 callback: function(){
32809 this.el.setLeftTop(-10000, -10000);
32811 this.afterSlideIn();
32819 slideInIf : function(e){
32820 if(!e.within(this.el)){
32825 animateCollapse : function(){
32826 this.beforeSlide();
32827 this.el.setStyle("z-index", 20000);
32828 var anchor = this.getSlideAnchor();
32829 this.el.slideOut(anchor, {
32830 callback : function(){
32831 this.el.setStyle("z-index", "");
32832 this.collapsedEl.slideIn(anchor, {duration:.3});
32834 this.el.setLocation(-10000,-10000);
32836 this.fireEvent("collapsed", this);
32843 animateExpand : function(){
32844 this.beforeSlide();
32845 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32846 this.el.setStyle("z-index", 20000);
32847 this.collapsedEl.hide({
32850 this.el.slideIn(this.getSlideAnchor(), {
32851 callback : function(){
32852 this.el.setStyle("z-index", "");
32855 this.split.el.show();
32857 this.fireEvent("invalidated", this);
32858 this.fireEvent("expanded", this);
32886 getAnchor : function(){
32887 return this.anchors[this.position];
32890 getCollapseAnchor : function(){
32891 return this.canchors[this.position];
32894 getSlideAnchor : function(){
32895 return this.sanchors[this.position];
32898 getAlignAdj : function(){
32899 var cm = this.cmargins;
32900 switch(this.position){
32916 getExpandAdj : function(){
32917 var c = this.collapsedEl, cm = this.cmargins;
32918 switch(this.position){
32920 return [-(cm.right+c.getWidth()+cm.left), 0];
32923 return [cm.right+c.getWidth()+cm.left, 0];
32926 return [0, -(cm.top+cm.bottom+c.getHeight())];
32929 return [0, cm.top+cm.bottom+c.getHeight()];
32935 * Ext JS Library 1.1.1
32936 * Copyright(c) 2006-2007, Ext JS, LLC.
32938 * Originally Released Under LGPL - original licence link has changed is not relivant.
32941 * <script type="text/javascript">
32944 * These classes are private internal classes
32946 Roo.bootstrap.layout.Center = function(config){
32947 config.region = "center";
32948 Roo.bootstrap.layout.Region.call(this, config);
32949 this.visible = true;
32950 this.minWidth = config.minWidth || 20;
32951 this.minHeight = config.minHeight || 20;
32954 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
32956 // center panel can't be hidden
32960 // center panel can't be hidden
32963 getMinWidth: function(){
32964 return this.minWidth;
32967 getMinHeight: function(){
32968 return this.minHeight;
32981 Roo.bootstrap.layout.North = function(config)
32983 config.region = 'north';
32984 config.cursor = 'n-resize';
32986 Roo.bootstrap.layout.Split.call(this, config);
32988 this.split.placement = Roo.bootstrap.SplitBar.TOP;
32989 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
32990 this.split.el.addClass("roo-layout-split-v");
32992 var size = config.initialSize || config.height;
32993 if(typeof size != "undefined"){
32994 this.el.setHeight(size);
32997 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
32999 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33000 getBox : function(){
33001 if(this.collapsed){
33002 return this.collapsedEl.getBox();
33004 var box = this.el.getBox();
33006 box.height += this.split.el.getHeight();
33011 updateBox : function(box){
33012 if(this.split && !this.collapsed){
33013 box.height -= this.split.el.getHeight();
33014 this.split.el.setLeft(box.x);
33015 this.split.el.setTop(box.y+box.height);
33016 this.split.el.setWidth(box.width);
33018 if(this.collapsed){
33019 this.updateBody(box.width, null);
33021 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33029 Roo.bootstrap.layout.South = function(config){
33030 config.region = 'south';
33031 config.cursor = 's-resize';
33032 Roo.bootstrap.layout.Split.call(this, config);
33034 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33035 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33036 this.split.el.addClass("roo-layout-split-v");
33038 var size = config.initialSize || config.height;
33039 if(typeof size != "undefined"){
33040 this.el.setHeight(size);
33044 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33045 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33046 getBox : function(){
33047 if(this.collapsed){
33048 return this.collapsedEl.getBox();
33050 var box = this.el.getBox();
33052 var sh = this.split.el.getHeight();
33059 updateBox : function(box){
33060 if(this.split && !this.collapsed){
33061 var sh = this.split.el.getHeight();
33064 this.split.el.setLeft(box.x);
33065 this.split.el.setTop(box.y-sh);
33066 this.split.el.setWidth(box.width);
33068 if(this.collapsed){
33069 this.updateBody(box.width, null);
33071 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33075 Roo.bootstrap.layout.East = function(config){
33076 config.region = "east";
33077 config.cursor = "e-resize";
33078 Roo.bootstrap.layout.Split.call(this, config);
33080 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33081 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33082 this.split.el.addClass("roo-layout-split-h");
33084 var size = config.initialSize || config.width;
33085 if(typeof size != "undefined"){
33086 this.el.setWidth(size);
33089 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33090 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33091 getBox : function(){
33092 if(this.collapsed){
33093 return this.collapsedEl.getBox();
33095 var box = this.el.getBox();
33097 var sw = this.split.el.getWidth();
33104 updateBox : function(box){
33105 if(this.split && !this.collapsed){
33106 var sw = this.split.el.getWidth();
33108 this.split.el.setLeft(box.x);
33109 this.split.el.setTop(box.y);
33110 this.split.el.setHeight(box.height);
33113 if(this.collapsed){
33114 this.updateBody(null, box.height);
33116 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33120 Roo.bootstrap.layout.West = function(config){
33121 config.region = "west";
33122 config.cursor = "w-resize";
33124 Roo.bootstrap.layout.Split.call(this, config);
33126 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33127 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33128 this.split.el.addClass("roo-layout-split-h");
33130 var size = config.initialSize || config.width;
33131 if(typeof size != "undefined"){
33132 this.el.setWidth(size);
33135 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33136 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33137 getBox : function(){
33138 if(this.collapsed){
33139 return this.collapsedEl.getBox();
33141 var box = this.el.getBox();
33143 box.width += this.split.el.getWidth();
33148 updateBox : function(box){
33149 if(this.split && !this.collapsed){
33150 var sw = this.split.el.getWidth();
33152 this.split.el.setLeft(box.x+box.width);
33153 this.split.el.setTop(box.y);
33154 this.split.el.setHeight(box.height);
33156 if(this.collapsed){
33157 this.updateBody(null, box.height);
33159 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33162 Roo.namespace("Roo.bootstrap.panel");/*
33164 * Ext JS Library 1.1.1
33165 * Copyright(c) 2006-2007, Ext JS, LLC.
33167 * Originally Released Under LGPL - original licence link has changed is not relivant.
33170 * <script type="text/javascript">
33173 * @class Roo.ContentPanel
33174 * @extends Roo.util.Observable
33175 * A basic ContentPanel element.
33176 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33177 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33178 * @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
33179 * @cfg {Boolean} closable True if the panel can be closed/removed
33180 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33181 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33182 * @cfg {Toolbar} toolbar A toolbar for this panel
33183 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33184 * @cfg {String} title The title for this panel
33185 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33186 * @cfg {String} url Calls {@link #setUrl} with this value
33187 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33188 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33189 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33190 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33193 * Create a new ContentPanel.
33194 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33195 * @param {String/Object} config A string to set only the title or a config object
33196 * @param {String} content (optional) Set the HTML content for this panel
33197 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33199 Roo.bootstrap.panel.Content = function( config){
33201 var el = config.el;
33202 var content = config.content;
33204 if(config.autoCreate){ // xtype is available if this is called from factory
33207 this.el = Roo.get(el);
33208 if(!this.el && config && config.autoCreate){
33209 if(typeof config.autoCreate == "object"){
33210 if(!config.autoCreate.id){
33211 config.autoCreate.id = config.id||el;
33213 this.el = Roo.DomHelper.append(document.body,
33214 config.autoCreate, true);
33216 var elcfg = { tag: "div",
33217 cls: "roo-layout-inactive-content",
33221 elcfg.html = config.html;
33225 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33228 this.closable = false;
33229 this.loaded = false;
33230 this.active = false;
33231 if(typeof config == "string"){
33232 this.title = config;
33234 Roo.apply(this, config);
33237 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33238 this.wrapEl = this.el.wrap();
33239 this.toolbar.container = this.el.insertSibling(false, 'before');
33240 this.toolbar = new Roo.Toolbar(this.toolbar);
33243 // xtype created footer. - not sure if will work as we normally have to render first..
33244 if (this.footer && !this.footer.el && this.footer.xtype) {
33245 if (!this.wrapEl) {
33246 this.wrapEl = this.el.wrap();
33249 this.footer.container = this.wrapEl.createChild();
33251 this.footer = Roo.factory(this.footer, Roo);
33256 this.resizeEl = Roo.get(this.resizeEl, true);
33258 this.resizeEl = this.el;
33260 // handle view.xtype
33268 * Fires when this panel is activated.
33269 * @param {Roo.ContentPanel} this
33273 * @event deactivate
33274 * Fires when this panel is activated.
33275 * @param {Roo.ContentPanel} this
33277 "deactivate" : true,
33281 * Fires when this panel is resized if fitToFrame is true.
33282 * @param {Roo.ContentPanel} this
33283 * @param {Number} width The width after any component adjustments
33284 * @param {Number} height The height after any component adjustments
33290 * Fires when this tab is created
33291 * @param {Roo.ContentPanel} this
33302 if(this.autoScroll){
33303 this.resizeEl.setStyle("overflow", "auto");
33305 // fix randome scrolling
33306 this.el.on('scroll', function() {
33307 Roo.log('fix random scolling');
33308 this.scrollTo('top',0);
33311 content = content || this.content;
33313 this.setContent(content);
33315 if(config && config.url){
33316 this.setUrl(this.url, this.params, this.loadOnce);
33321 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33323 if (this.view && typeof(this.view.xtype) != 'undefined') {
33324 this.view.el = this.el.appendChild(document.createElement("div"));
33325 this.view = Roo.factory(this.view);
33326 this.view.render && this.view.render(false, '');
33330 this.fireEvent('render', this);
33333 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33335 setRegion : function(region){
33336 this.region = region;
33338 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33340 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33345 * Returns the toolbar for this Panel if one was configured.
33346 * @return {Roo.Toolbar}
33348 getToolbar : function(){
33349 return this.toolbar;
33352 setActiveState : function(active){
33353 this.active = active;
33355 this.fireEvent("deactivate", this);
33357 this.fireEvent("activate", this);
33361 * Updates this panel's element
33362 * @param {String} content The new content
33363 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33365 setContent : function(content, loadScripts){
33366 this.el.update(content, loadScripts);
33369 ignoreResize : function(w, h){
33370 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33373 this.lastSize = {width: w, height: h};
33378 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33379 * @return {Roo.UpdateManager} The UpdateManager
33381 getUpdateManager : function(){
33382 return this.el.getUpdateManager();
33385 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33386 * @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:
33389 url: "your-url.php",
33390 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33391 callback: yourFunction,
33392 scope: yourObject, //(optional scope)
33395 text: "Loading...",
33400 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33401 * 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.
33402 * @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}
33403 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33404 * @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.
33405 * @return {Roo.ContentPanel} this
33408 var um = this.el.getUpdateManager();
33409 um.update.apply(um, arguments);
33415 * 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.
33416 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33417 * @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)
33418 * @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)
33419 * @return {Roo.UpdateManager} The UpdateManager
33421 setUrl : function(url, params, loadOnce){
33422 if(this.refreshDelegate){
33423 this.removeListener("activate", this.refreshDelegate);
33425 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33426 this.on("activate", this.refreshDelegate);
33427 return this.el.getUpdateManager();
33430 _handleRefresh : function(url, params, loadOnce){
33431 if(!loadOnce || !this.loaded){
33432 var updater = this.el.getUpdateManager();
33433 updater.update(url, params, this._setLoaded.createDelegate(this));
33437 _setLoaded : function(){
33438 this.loaded = true;
33442 * Returns this panel's id
33445 getId : function(){
33450 * Returns this panel's element - used by regiosn to add.
33451 * @return {Roo.Element}
33453 getEl : function(){
33454 return this.wrapEl || this.el;
33459 adjustForComponents : function(width, height)
33461 //Roo.log('adjustForComponents ');
33462 if(this.resizeEl != this.el){
33463 width -= this.el.getFrameWidth('lr');
33464 height -= this.el.getFrameWidth('tb');
33467 var te = this.toolbar.getEl();
33468 height -= te.getHeight();
33469 te.setWidth(width);
33472 var te = this.footer.getEl();
33473 Roo.log("footer:" + te.getHeight());
33475 height -= te.getHeight();
33476 te.setWidth(width);
33480 if(this.adjustments){
33481 width += this.adjustments[0];
33482 height += this.adjustments[1];
33484 return {"width": width, "height": height};
33487 setSize : function(width, height){
33488 if(this.fitToFrame && !this.ignoreResize(width, height)){
33489 if(this.fitContainer && this.resizeEl != this.el){
33490 this.el.setSize(width, height);
33492 var size = this.adjustForComponents(width, height);
33493 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33494 this.fireEvent('resize', this, size.width, size.height);
33499 * Returns this panel's title
33502 getTitle : function(){
33507 * Set this panel's title
33508 * @param {String} title
33510 setTitle : function(title){
33511 this.title = title;
33513 this.region.updatePanelTitle(this, title);
33518 * Returns true is this panel was configured to be closable
33519 * @return {Boolean}
33521 isClosable : function(){
33522 return this.closable;
33525 beforeSlide : function(){
33527 this.resizeEl.clip();
33530 afterSlide : function(){
33532 this.resizeEl.unclip();
33536 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33537 * Will fail silently if the {@link #setUrl} method has not been called.
33538 * This does not activate the panel, just updates its content.
33540 refresh : function(){
33541 if(this.refreshDelegate){
33542 this.loaded = false;
33543 this.refreshDelegate();
33548 * Destroys this panel
33550 destroy : function(){
33551 this.el.removeAllListeners();
33552 var tempEl = document.createElement("span");
33553 tempEl.appendChild(this.el.dom);
33554 tempEl.innerHTML = "";
33560 * form - if the content panel contains a form - this is a reference to it.
33561 * @type {Roo.form.Form}
33565 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33566 * This contains a reference to it.
33572 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33582 * @param {Object} cfg Xtype definition of item to add.
33586 getChildContainer: function () {
33587 return this.getEl();
33592 var ret = new Roo.factory(cfg);
33597 if (cfg.xtype.match(/^Form$/)) {
33600 //if (this.footer) {
33601 // el = this.footer.container.insertSibling(false, 'before');
33603 el = this.el.createChild();
33606 this.form = new Roo.form.Form(cfg);
33609 if ( this.form.allItems.length) {
33610 this.form.render(el.dom);
33614 // should only have one of theses..
33615 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33616 // views.. should not be just added - used named prop 'view''
33618 cfg.el = this.el.appendChild(document.createElement("div"));
33621 var ret = new Roo.factory(cfg);
33623 ret.render && ret.render(false, ''); // render blank..
33633 * @class Roo.bootstrap.panel.Nest
33634 * @extends Roo.bootstrap.panel.Content
33636 * Create a new Panel, that can contain a layout.Border.
33639 * @param {Roo.BorderLayout} layout The layout for this panel
33640 * @param {String/Object} config A string to set only the title or a config object
33642 Roo.bootstrap.panel.Nest = function(config)
33644 // construct with only one argument..
33645 /* FIXME - implement nicer consturctors
33646 if (layout.layout) {
33648 layout = config.layout;
33649 delete config.layout;
33651 if (layout.xtype && !layout.getEl) {
33652 // then layout needs constructing..
33653 layout = Roo.factory(layout, Roo);
33657 config.el = config.layout.getEl();
33659 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33661 config.layout.monitorWindowResize = false; // turn off autosizing
33662 this.layout = config.layout;
33663 this.layout.getEl().addClass("roo-layout-nested-layout");
33670 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33672 setSize : function(width, height){
33673 if(!this.ignoreResize(width, height)){
33674 var size = this.adjustForComponents(width, height);
33675 var el = this.layout.getEl();
33676 el.setSize(size.width, size.height);
33677 var touch = el.dom.offsetWidth;
33678 this.layout.layout();
33679 // ie requires a double layout on the first pass
33680 if(Roo.isIE && !this.initialized){
33681 this.initialized = true;
33682 this.layout.layout();
33687 // activate all subpanels if not currently active..
33689 setActiveState : function(active){
33690 this.active = active;
33692 this.fireEvent("deactivate", this);
33696 this.fireEvent("activate", this);
33697 // not sure if this should happen before or after..
33698 if (!this.layout) {
33699 return; // should not happen..
33702 for (var r in this.layout.regions) {
33703 reg = this.layout.getRegion(r);
33704 if (reg.getActivePanel()) {
33705 //reg.showPanel(reg.getActivePanel()); // force it to activate..
33706 reg.setActivePanel(reg.getActivePanel());
33709 if (!reg.panels.length) {
33712 reg.showPanel(reg.getPanel(0));
33721 * Returns the nested BorderLayout for this panel
33722 * @return {Roo.BorderLayout}
33724 getLayout : function(){
33725 return this.layout;
33729 * Adds a xtype elements to the layout of the nested panel
33733 xtype : 'ContentPanel',
33740 xtype : 'NestedLayoutPanel',
33746 items : [ ... list of content panels or nested layout panels.. ]
33750 * @param {Object} cfg Xtype definition of item to add.
33752 addxtype : function(cfg) {
33753 return this.layout.addxtype(cfg);
33758 * Ext JS Library 1.1.1
33759 * Copyright(c) 2006-2007, Ext JS, LLC.
33761 * Originally Released Under LGPL - original licence link has changed is not relivant.
33764 * <script type="text/javascript">
33767 * @class Roo.TabPanel
33768 * @extends Roo.util.Observable
33769 * A lightweight tab container.
33773 // basic tabs 1, built from existing content
33774 var tabs = new Roo.TabPanel("tabs1");
33775 tabs.addTab("script", "View Script");
33776 tabs.addTab("markup", "View Markup");
33777 tabs.activate("script");
33779 // more advanced tabs, built from javascript
33780 var jtabs = new Roo.TabPanel("jtabs");
33781 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33783 // set up the UpdateManager
33784 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33785 var updater = tab2.getUpdateManager();
33786 updater.setDefaultUrl("ajax1.htm");
33787 tab2.on('activate', updater.refresh, updater, true);
33789 // Use setUrl for Ajax loading
33790 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33791 tab3.setUrl("ajax2.htm", null, true);
33794 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33797 jtabs.activate("jtabs-1");
33800 * Create a new TabPanel.
33801 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33802 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33804 Roo.bootstrap.panel.Tabs = function(config){
33806 * The container element for this TabPanel.
33807 * @type Roo.Element
33809 this.el = Roo.get(config.el);
33812 if(typeof config == "boolean"){
33813 this.tabPosition = config ? "bottom" : "top";
33815 Roo.apply(this, config);
33819 if(this.tabPosition == "bottom"){
33820 this.bodyEl = Roo.get(this.createBody(this.el.dom));
33821 this.el.addClass("roo-tabs-bottom");
33823 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33824 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33825 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33827 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33829 if(this.tabPosition != "bottom"){
33830 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
33831 * @type Roo.Element
33833 this.bodyEl = Roo.get(this.createBody(this.el.dom));
33834 this.el.addClass("roo-tabs-top");
33838 this.bodyEl.setStyle("position", "relative");
33840 this.active = null;
33841 this.activateDelegate = this.activate.createDelegate(this);
33846 * Fires when the active tab changes
33847 * @param {Roo.TabPanel} this
33848 * @param {Roo.TabPanelItem} activePanel The new active tab
33852 * @event beforetabchange
33853 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
33854 * @param {Roo.TabPanel} this
33855 * @param {Object} e Set cancel to true on this object to cancel the tab change
33856 * @param {Roo.TabPanelItem} tab The tab being changed to
33858 "beforetabchange" : true
33861 Roo.EventManager.onWindowResize(this.onResize, this);
33862 this.cpad = this.el.getPadding("lr");
33863 this.hiddenCount = 0;
33866 // toolbar on the tabbar support...
33867 if (this.toolbar) {
33868 alert("no toolbar support yet");
33869 this.toolbar = false;
33871 var tcfg = this.toolbar;
33872 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
33873 this.toolbar = new Roo.Toolbar(tcfg);
33874 if (Roo.isSafari) {
33875 var tbl = tcfg.container.child('table', true);
33876 tbl.setAttribute('width', '100%');
33884 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
33887 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
33889 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
33891 tabPosition : "top",
33893 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
33895 currentTabWidth : 0,
33897 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
33901 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
33905 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
33907 preferredTabWidth : 175,
33909 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
33911 resizeTabs : false,
33913 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
33915 monitorResize : true,
33917 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
33922 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
33923 * @param {String} id The id of the div to use <b>or create</b>
33924 * @param {String} text The text for the tab
33925 * @param {String} content (optional) Content to put in the TabPanelItem body
33926 * @param {Boolean} closable (optional) True to create a close icon on the tab
33927 * @return {Roo.TabPanelItem} The created TabPanelItem
33929 addTab : function(id, text, content, closable)
33931 var item = new Roo.bootstrap.panel.TabItem({
33935 closable : closable
33937 this.addTabItem(item);
33939 item.setContent(content);
33945 * Returns the {@link Roo.TabPanelItem} with the specified id/index
33946 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
33947 * @return {Roo.TabPanelItem}
33949 getTab : function(id){
33950 return this.items[id];
33954 * Hides the {@link Roo.TabPanelItem} with the specified id/index
33955 * @param {String/Number} id The id or index of the TabPanelItem to hide.
33957 hideTab : function(id){
33958 var t = this.items[id];
33961 this.hiddenCount++;
33962 this.autoSizeTabs();
33967 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
33968 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
33970 unhideTab : function(id){
33971 var t = this.items[id];
33973 t.setHidden(false);
33974 this.hiddenCount--;
33975 this.autoSizeTabs();
33980 * Adds an existing {@link Roo.TabPanelItem}.
33981 * @param {Roo.TabPanelItem} item The TabPanelItem to add
33983 addTabItem : function(item){
33984 this.items[item.id] = item;
33985 this.items.push(item);
33986 // if(this.resizeTabs){
33987 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
33988 // this.autoSizeTabs();
33990 // item.autoSize();
33995 * Removes a {@link Roo.TabPanelItem}.
33996 * @param {String/Number} id The id or index of the TabPanelItem to remove.
33998 removeTab : function(id){
33999 var items = this.items;
34000 var tab = items[id];
34001 if(!tab) { return; }
34002 var index = items.indexOf(tab);
34003 if(this.active == tab && items.length > 1){
34004 var newTab = this.getNextAvailable(index);
34009 this.stripEl.dom.removeChild(tab.pnode.dom);
34010 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34011 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34013 items.splice(index, 1);
34014 delete this.items[tab.id];
34015 tab.fireEvent("close", tab);
34016 tab.purgeListeners();
34017 this.autoSizeTabs();
34020 getNextAvailable : function(start){
34021 var items = this.items;
34023 // look for a next tab that will slide over to
34024 // replace the one being removed
34025 while(index < items.length){
34026 var item = items[++index];
34027 if(item && !item.isHidden()){
34031 // if one isn't found select the previous tab (on the left)
34034 var item = items[--index];
34035 if(item && !item.isHidden()){
34043 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34044 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34046 disableTab : function(id){
34047 var tab = this.items[id];
34048 if(tab && this.active != tab){
34054 * Enables a {@link Roo.TabPanelItem} that is disabled.
34055 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34057 enableTab : function(id){
34058 var tab = this.items[id];
34063 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34064 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34065 * @return {Roo.TabPanelItem} The TabPanelItem.
34067 activate : function(id){
34068 var tab = this.items[id];
34072 if(tab == this.active || tab.disabled){
34076 this.fireEvent("beforetabchange", this, e, tab);
34077 if(e.cancel !== true && !tab.disabled){
34079 this.active.hide();
34081 this.active = this.items[id];
34082 this.active.show();
34083 this.fireEvent("tabchange", this, this.active);
34089 * Gets the active {@link Roo.TabPanelItem}.
34090 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34092 getActiveTab : function(){
34093 return this.active;
34097 * Updates the tab body element to fit the height of the container element
34098 * for overflow scrolling
34099 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34101 syncHeight : function(targetHeight){
34102 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34103 var bm = this.bodyEl.getMargins();
34104 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34105 this.bodyEl.setHeight(newHeight);
34109 onResize : function(){
34110 if(this.monitorResize){
34111 this.autoSizeTabs();
34116 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34118 beginUpdate : function(){
34119 this.updating = true;
34123 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34125 endUpdate : function(){
34126 this.updating = false;
34127 this.autoSizeTabs();
34131 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34133 autoSizeTabs : function(){
34134 var count = this.items.length;
34135 var vcount = count - this.hiddenCount;
34136 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34139 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34140 var availWidth = Math.floor(w / vcount);
34141 var b = this.stripBody;
34142 if(b.getWidth() > w){
34143 var tabs = this.items;
34144 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34145 if(availWidth < this.minTabWidth){
34146 /*if(!this.sleft){ // incomplete scrolling code
34147 this.createScrollButtons();
34150 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34153 if(this.currentTabWidth < this.preferredTabWidth){
34154 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34160 * Returns the number of tabs in this TabPanel.
34163 getCount : function(){
34164 return this.items.length;
34168 * Resizes all the tabs to the passed width
34169 * @param {Number} The new width
34171 setTabWidth : function(width){
34172 this.currentTabWidth = width;
34173 for(var i = 0, len = this.items.length; i < len; i++) {
34174 if(!this.items[i].isHidden()) {
34175 this.items[i].setWidth(width);
34181 * Destroys this TabPanel
34182 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34184 destroy : function(removeEl){
34185 Roo.EventManager.removeResizeListener(this.onResize, this);
34186 for(var i = 0, len = this.items.length; i < len; i++){
34187 this.items[i].purgeListeners();
34189 if(removeEl === true){
34190 this.el.update("");
34195 createStrip : function(container)
34197 var strip = document.createElement("nav");
34198 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34199 container.appendChild(strip);
34203 createStripList : function(strip)
34205 // div wrapper for retard IE
34206 // returns the "tr" element.
34207 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34208 //'<div class="x-tabs-strip-wrap">'+
34209 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34210 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34211 return strip.firstChild; //.firstChild.firstChild.firstChild;
34213 createBody : function(container)
34215 var body = document.createElement("div");
34216 Roo.id(body, "tab-body");
34217 //Roo.fly(body).addClass("x-tabs-body");
34218 Roo.fly(body).addClass("tab-content");
34219 container.appendChild(body);
34222 createItemBody :function(bodyEl, id){
34223 var body = Roo.getDom(id);
34225 body = document.createElement("div");
34228 //Roo.fly(body).addClass("x-tabs-item-body");
34229 Roo.fly(body).addClass("tab-pane");
34230 bodyEl.insertBefore(body, bodyEl.firstChild);
34234 createStripElements : function(stripEl, text, closable)
34236 var td = document.createElement("li"); // was td..
34237 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34238 //stripEl.appendChild(td);
34240 td.className = "x-tabs-closable";
34241 if(!this.closeTpl){
34242 this.closeTpl = new Roo.Template(
34243 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34244 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34245 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34248 var el = this.closeTpl.overwrite(td, {"text": text});
34249 var close = el.getElementsByTagName("div")[0];
34250 var inner = el.getElementsByTagName("em")[0];
34251 return {"el": el, "close": close, "inner": inner};
34254 // not sure what this is..
34256 //this.tabTpl = new Roo.Template(
34257 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34258 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34260 this.tabTpl = new Roo.Template(
34262 '<span unselectable="on"' +
34263 (this.disableTooltips ? '' : ' title="{text}"') +
34264 ' >{text}</span></span></a>'
34268 var el = this.tabTpl.overwrite(td, {"text": text});
34269 var inner = el.getElementsByTagName("span")[0];
34270 return {"el": el, "inner": inner};
34278 * @class Roo.TabPanelItem
34279 * @extends Roo.util.Observable
34280 * Represents an individual item (tab plus body) in a TabPanel.
34281 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34282 * @param {String} id The id of this TabPanelItem
34283 * @param {String} text The text for the tab of this TabPanelItem
34284 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34286 Roo.bootstrap.panel.TabItem = function(config){
34288 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34289 * @type Roo.TabPanel
34291 this.tabPanel = config.panel;
34293 * The id for this TabPanelItem
34296 this.id = config.id;
34298 this.disabled = false;
34300 this.text = config.text;
34302 this.loaded = false;
34303 this.closable = config.closable;
34306 * The body element for this TabPanelItem.
34307 * @type Roo.Element
34309 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34310 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34311 this.bodyEl.setStyle("display", "block");
34312 this.bodyEl.setStyle("zoom", "1");
34313 //this.hideAction();
34315 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34317 this.el = Roo.get(els.el);
34318 this.inner = Roo.get(els.inner, true);
34319 this.textEl = Roo.get(this.el.dom.firstChild, true);
34320 this.pnode = Roo.get(els.el.parentNode, true);
34321 this.el.on("mousedown", this.onTabMouseDown, this);
34322 this.el.on("click", this.onTabClick, this);
34324 if(config.closable){
34325 var c = Roo.get(els.close, true);
34326 c.dom.title = this.closeText;
34327 c.addClassOnOver("close-over");
34328 c.on("click", this.closeClick, this);
34334 * Fires when this tab becomes the active tab.
34335 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34336 * @param {Roo.TabPanelItem} this
34340 * @event beforeclose
34341 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34342 * @param {Roo.TabPanelItem} this
34343 * @param {Object} e Set cancel to true on this object to cancel the close.
34345 "beforeclose": true,
34348 * Fires when this tab is closed.
34349 * @param {Roo.TabPanelItem} this
34353 * @event deactivate
34354 * Fires when this tab is no longer the active tab.
34355 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34356 * @param {Roo.TabPanelItem} this
34358 "deactivate" : true
34360 this.hidden = false;
34362 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34365 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34367 purgeListeners : function(){
34368 Roo.util.Observable.prototype.purgeListeners.call(this);
34369 this.el.removeAllListeners();
34372 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34375 this.pnode.addClass("active");
34378 this.tabPanel.stripWrap.repaint();
34380 this.fireEvent("activate", this.tabPanel, this);
34384 * Returns true if this tab is the active tab.
34385 * @return {Boolean}
34387 isActive : function(){
34388 return this.tabPanel.getActiveTab() == this;
34392 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34395 this.pnode.removeClass("active");
34397 this.fireEvent("deactivate", this.tabPanel, this);
34400 hideAction : function(){
34401 this.bodyEl.hide();
34402 this.bodyEl.setStyle("position", "absolute");
34403 this.bodyEl.setLeft("-20000px");
34404 this.bodyEl.setTop("-20000px");
34407 showAction : function(){
34408 this.bodyEl.setStyle("position", "relative");
34409 this.bodyEl.setTop("");
34410 this.bodyEl.setLeft("");
34411 this.bodyEl.show();
34415 * Set the tooltip for the tab.
34416 * @param {String} tooltip The tab's tooltip
34418 setTooltip : function(text){
34419 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34420 this.textEl.dom.qtip = text;
34421 this.textEl.dom.removeAttribute('title');
34423 this.textEl.dom.title = text;
34427 onTabClick : function(e){
34428 e.preventDefault();
34429 this.tabPanel.activate(this.id);
34432 onTabMouseDown : function(e){
34433 e.preventDefault();
34434 this.tabPanel.activate(this.id);
34437 getWidth : function(){
34438 return this.inner.getWidth();
34441 setWidth : function(width){
34442 var iwidth = width - this.pnode.getPadding("lr");
34443 this.inner.setWidth(iwidth);
34444 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34445 this.pnode.setWidth(width);
34449 * Show or hide the tab
34450 * @param {Boolean} hidden True to hide or false to show.
34452 setHidden : function(hidden){
34453 this.hidden = hidden;
34454 this.pnode.setStyle("display", hidden ? "none" : "");
34458 * Returns true if this tab is "hidden"
34459 * @return {Boolean}
34461 isHidden : function(){
34462 return this.hidden;
34466 * Returns the text for this tab
34469 getText : function(){
34473 autoSize : function(){
34474 //this.el.beginMeasure();
34475 this.textEl.setWidth(1);
34477 * #2804 [new] Tabs in Roojs
34478 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34480 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34481 //this.el.endMeasure();
34485 * Sets the text for the tab (Note: this also sets the tooltip text)
34486 * @param {String} text The tab's text and tooltip
34488 setText : function(text){
34490 this.textEl.update(text);
34491 this.setTooltip(text);
34492 //if(!this.tabPanel.resizeTabs){
34493 // this.autoSize();
34497 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34499 activate : function(){
34500 this.tabPanel.activate(this.id);
34504 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34506 disable : function(){
34507 if(this.tabPanel.active != this){
34508 this.disabled = true;
34509 this.pnode.addClass("disabled");
34514 * Enables this TabPanelItem if it was previously disabled.
34516 enable : function(){
34517 this.disabled = false;
34518 this.pnode.removeClass("disabled");
34522 * Sets the content for this TabPanelItem.
34523 * @param {String} content The content
34524 * @param {Boolean} loadScripts true to look for and load scripts
34526 setContent : function(content, loadScripts){
34527 this.bodyEl.update(content, loadScripts);
34531 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34532 * @return {Roo.UpdateManager} The UpdateManager
34534 getUpdateManager : function(){
34535 return this.bodyEl.getUpdateManager();
34539 * Set a URL to be used to load the content for this TabPanelItem.
34540 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34541 * @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)
34542 * @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)
34543 * @return {Roo.UpdateManager} The UpdateManager
34545 setUrl : function(url, params, loadOnce){
34546 if(this.refreshDelegate){
34547 this.un('activate', this.refreshDelegate);
34549 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34550 this.on("activate", this.refreshDelegate);
34551 return this.bodyEl.getUpdateManager();
34555 _handleRefresh : function(url, params, loadOnce){
34556 if(!loadOnce || !this.loaded){
34557 var updater = this.bodyEl.getUpdateManager();
34558 updater.update(url, params, this._setLoaded.createDelegate(this));
34563 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
34564 * Will fail silently if the setUrl method has not been called.
34565 * This does not activate the panel, just updates its content.
34567 refresh : function(){
34568 if(this.refreshDelegate){
34569 this.loaded = false;
34570 this.refreshDelegate();
34575 _setLoaded : function(){
34576 this.loaded = true;
34580 closeClick : function(e){
34583 this.fireEvent("beforeclose", this, o);
34584 if(o.cancel !== true){
34585 this.tabPanel.removeTab(this.id);
34589 * The text displayed in the tooltip for the close icon.
34592 closeText : "Close this tab"