2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fs
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
661 preventDefault: true,
669 getAutoCreate : function(){
677 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
683 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685 if (this.toggle == true) {
688 cls: 'slider-frame roo-button',
693 'data-off-text':'OFF',
694 cls: 'slider-button',
700 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' '+this.weight;
710 cfg["aria-hidden"] = true;
712 cfg.html = "×";
718 if (this.theme==='default') {
719 cfg.cls = 'btn roo-button';
721 //if (this.parentType != 'Navbar') {
722 this.weight = this.weight.length ? this.weight : 'default';
724 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728 cfg.cls += ' btn-' + outline + weight;
729 if (this.weight == 'default') {
731 cfg.cls += ' btn-' + this.weight;
734 } else if (this.theme==='glow') {
737 cfg.cls = 'btn-glow roo-button';
739 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741 cfg.cls += ' ' + this.weight;
747 this.cls += ' inverse';
751 if (this.active || this.pressed === true) {
752 cfg.cls += ' active';
756 cfg.disabled = 'disabled';
760 Roo.log('changing to ul' );
762 this.glyphicon = 'caret';
763 if (Roo.bootstrap.version == 4) {
764 this.fa = 'caret-down';
769 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771 //gsRoo.log(this.parentType);
772 if (this.parentType === 'Navbar' && !this.parent().bar) {
773 Roo.log('changing to li?');
782 href : this.href || '#'
785 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
786 cfg.cls += ' dropdown';
793 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
795 if (this.glyphicon) {
796 cfg.html = ' ' + cfg.html;
801 cls: 'glyphicon glyphicon-' + this.glyphicon
806 cfg.html = ' ' + cfg.html;
811 cls: 'fa fas fa-' + this.fa
821 // cfg.cls='btn roo-button';
825 var value = cfg.html;
830 cls: 'glyphicon glyphicon-' + this.glyphicon,
837 cls: 'fa fas fa-' + this.fa,
842 var bw = this.badge_weight.length ? this.badge_weight :
843 (this.weight.length ? this.weight : 'secondary');
844 bw = bw == 'default' ? 'secondary' : bw;
850 cls: 'badge badge-' + bw,
859 cfg.cls += ' dropdown';
860 cfg.html = typeof(cfg.html) != 'undefined' ?
861 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
864 if (cfg.tag !== 'a' && this.href !== '') {
865 throw "Tag must be a to set href.";
866 } else if (this.href.length > 0) {
867 cfg.href = this.href;
870 if(this.removeClass){
875 cfg.target = this.target;
880 initEvents: function() {
881 // Roo.log('init events?');
882 // Roo.log(this.el.dom);
885 if (typeof (this.menu) != 'undefined') {
886 this.menu.parentType = this.xtype;
887 this.menu.triggerEl = this.el;
888 this.addxtype(Roo.apply({}, this.menu));
892 if (this.el.hasClass('roo-button')) {
893 this.el.on('click', this.onClick, this);
895 this.el.select('.roo-button').on('click', this.onClick, this);
898 if(this.removeClass){
899 this.el.on('click', this.onClick, this);
902 this.el.enableDisplayMode();
905 onClick : function(e)
911 Roo.log('button on click ');
912 if(this.preventDefault){
916 if (this.pressed === true || this.pressed === false) {
917 this.toggleActive(e);
921 this.fireEvent('click', this, e);
925 * Enables this button
929 this.disabled = false;
930 this.el.removeClass('disabled');
934 * Disable this button
938 this.disabled = true;
939 this.el.addClass('disabled');
942 * sets the active state on/off,
943 * @param {Boolean} state (optional) Force a particular state
945 setActive : function(v) {
947 this.el[v ? 'addClass' : 'removeClass']('active');
951 * toggles the current active state
953 toggleActive : function(e)
955 this.setActive(!this.pressed);
956 this.fireEvent('toggle', this, e, !this.pressed);
959 * get the current active state
960 * @return {boolean} true if it's active
962 isActive : function()
964 return this.el.hasClass('active');
967 * set the text of the first selected button
969 setText : function(str)
971 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
974 * get the text of the first selected button
978 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
981 setWeight : function(str)
983 this.el.removeClass(this.weightClass);
985 var outline = this.outline ? 'outline-' : '';
986 if (str == 'default') {
987 this.el.addClass('btn-default btn-outline-secondary');
990 this.el.addClass('btn-' + outline + str);
1004 * @class Roo.bootstrap.Column
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Column class
1007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019 * @cfg {String} fa (ban|check|...) font awesome icon
1020 * @cfg {Number} fasize (1|2|....) font awsome size
1022 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {String} html content of column.
1027 * Create a new Column
1028 * @param {Object} config The config object
1031 Roo.bootstrap.Column = function(config){
1032 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1054 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1062 ['xs','sm','md','lg'].map(function(size){
1063 //Roo.log( size + ':' + settings[size]);
1065 if (settings[size+'off'] !== false) {
1066 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069 if (settings[size] === false) {
1073 if (!settings[size]) { // 0 = hidden
1074 cfg.cls += ' hidden-' + size;
1077 cfg.cls += ' col-' + size + '-' + settings[size];
1082 cfg.cls += ' hidden';
1085 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086 cfg.cls +=' alert alert-' + this.alert;
1090 if (this.html.length) {
1091 cfg.html = this.html;
1095 if (this.fasize > 1) {
1096 fasize = ' fa-' + this.fasize + 'x';
1098 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1103 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1122 * @class Roo.bootstrap.Container
1123 * @extends Roo.bootstrap.Component
1124 * Bootstrap Container class
1125 * @cfg {Boolean} jumbotron is it a jumbotron element
1126 * @cfg {String} html content of element
1127 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1129 * @cfg {String} header content of header (for panel)
1130 * @cfg {String} footer content of footer (for panel)
1131 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132 * @cfg {String} tag (header|aside|section) type of HTML tag.
1133 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134 * @cfg {String} fa font awesome icon
1135 * @cfg {String} icon (info-sign|check|...) glyphicon name
1136 * @cfg {Boolean} hidden (true|false) hide the element
1137 * @cfg {Boolean} expandable (true|false) default false
1138 * @cfg {Boolean} expanded (true|false) default true
1139 * @cfg {String} rheader contet on the right of header
1140 * @cfg {Boolean} clickable (true|false) default false
1144 * Create a new Container
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Container = function(config){
1149 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1155 * After the panel has been expand
1157 * @param {Roo.bootstrap.Container} this
1162 * After the panel has been collapsed
1164 * @param {Roo.bootstrap.Container} this
1169 * When a element is chick
1170 * @param {Roo.bootstrap.Container} this
1171 * @param {Roo.EventObject} e
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1195 getChildContainer : function() {
1201 if (this.panel.length) {
1202 return this.el.select('.panel-body',true).first();
1209 getAutoCreate : function(){
1212 tag : this.tag || 'div',
1216 if (this.jumbotron) {
1217 cfg.cls = 'jumbotron';
1222 // - this is applied by the parent..
1224 // cfg.cls = this.cls + '';
1227 if (this.sticky.length) {
1229 var bd = Roo.get(document.body);
1230 if (!bd.hasClass('bootstrap-sticky')) {
1231 bd.addClass('bootstrap-sticky');
1232 Roo.select('html',true).setStyle('height', '100%');
1235 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239 if (this.well.length) {
1240 switch (this.well) {
1243 cfg.cls +=' well well-' +this.well;
1252 cfg.cls += ' hidden';
1256 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257 cfg.cls +=' alert alert-' + this.alert;
1262 if (this.panel.length) {
1263 cfg.cls += ' panel panel-' + this.panel;
1265 if (this.header.length) {
1269 if(this.expandable){
1271 cfg.cls = cfg.cls + ' expandable';
1275 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1283 cls : 'panel-title',
1284 html : (this.expandable ? ' ' : '') + this.header
1288 cls: 'panel-header-right',
1294 cls : 'panel-heading',
1295 style : this.expandable ? 'cursor: pointer' : '',
1303 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1308 if (this.footer.length) {
1310 cls : 'panel-footer',
1319 body.html = this.html || cfg.html;
1320 // prefix with the icons..
1322 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1325 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1330 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331 cfg.cls = 'container';
1337 initEvents: function()
1339 if(this.expandable){
1340 var headerEl = this.headerEl();
1343 headerEl.on('click', this.onToggleClick, this);
1348 this.el.on('click', this.onClick, this);
1353 onToggleClick : function()
1355 var headerEl = this.headerEl();
1371 if(this.fireEvent('expand', this)) {
1373 this.expanded = true;
1375 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377 this.el.select('.panel-body',true).first().removeClass('hide');
1379 var toggleEl = this.toggleEl();
1385 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1390 collapse : function()
1392 if(this.fireEvent('collapse', this)) {
1394 this.expanded = false;
1396 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397 this.el.select('.panel-body',true).first().addClass('hide');
1399 var toggleEl = this.toggleEl();
1405 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409 toggleEl : function()
1411 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415 return this.el.select('.panel-heading .fa',true).first();
1418 headerEl : function()
1420 if(!this.el || !this.panel.length || !this.header.length){
1424 return this.el.select('.panel-heading',true).first()
1429 if(!this.el || !this.panel.length){
1433 return this.el.select('.panel-body',true).first()
1436 titleEl : function()
1438 if(!this.el || !this.panel.length || !this.header.length){
1442 return this.el.select('.panel-title',true).first();
1445 setTitle : function(v)
1447 var titleEl = this.titleEl();
1453 titleEl.dom.innerHTML = v;
1456 getTitle : function()
1459 var titleEl = this.titleEl();
1465 return titleEl.dom.innerHTML;
1468 setRightTitle : function(v)
1470 var t = this.el.select('.panel-header-right',true).first();
1476 t.dom.innerHTML = v;
1479 onClick : function(e)
1483 this.fireEvent('click', this, e);
1496 * @class Roo.bootstrap.Img
1497 * @extends Roo.bootstrap.Component
1498 * Bootstrap Img class
1499 * @cfg {Boolean} imgResponsive false | true
1500 * @cfg {String} border rounded | circle | thumbnail
1501 * @cfg {String} src image source
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505 * @cfg {String} xsUrl xs image source
1506 * @cfg {String} smUrl sm image source
1507 * @cfg {String} mdUrl md image source
1508 * @cfg {String} lgUrl lg image source
1511 * Create a new Input
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Img = function(config){
1516 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1522 * The img click event for the img.
1523 * @param {Roo.EventObject} e
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1531 imgResponsive: true,
1541 getAutoCreate : function()
1543 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544 return this.createSingleImg();
1549 cls: 'roo-image-responsive-group',
1554 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556 if(!_this[size + 'Url']){
1562 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563 html: _this.html || cfg.html,
1564 src: _this[size + 'Url']
1567 img.cls += ' roo-image-responsive-' + size;
1569 var s = ['xs', 'sm', 'md', 'lg'];
1571 s.splice(s.indexOf(size), 1);
1573 Roo.each(s, function(ss){
1574 img.cls += ' hidden-' + ss;
1577 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578 cfg.cls += ' img-' + _this.border;
1582 cfg.alt = _this.alt;
1595 a.target = _this.target;
1599 cfg.cn.push((_this.href) ? a : img);
1606 createSingleImg : function()
1610 cls: (this.imgResponsive) ? 'img-responsive' : '',
1612 src : 'about:blank' // just incase src get's set to undefined?!?
1615 cfg.html = this.html || cfg.html;
1617 cfg.src = this.src || cfg.src;
1619 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620 cfg.cls += ' img-' + this.border;
1637 a.target = this.target;
1642 return (this.href) ? a : cfg;
1645 initEvents: function()
1648 this.el.on('click', this.onClick, this);
1653 onClick : function(e)
1655 Roo.log('img onclick');
1656 this.fireEvent('click', this, e);
1659 * Sets the url of the image - used to update it
1660 * @param {String} url the url of the image
1663 setSrc : function(url)
1667 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668 this.el.dom.src = url;
1672 this.el.select('img', true).first().dom.src = url;
1688 * @class Roo.bootstrap.Link
1689 * @extends Roo.bootstrap.Component
1690 * Bootstrap Link Class
1691 * @cfg {String} alt image alternative text
1692 * @cfg {String} href a tag href
1693 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694 * @cfg {String} html the content of the link.
1695 * @cfg {String} anchor name for the anchor link
1696 * @cfg {String} fa - favicon
1698 * @cfg {Boolean} preventDefault (true | false) default false
1702 * Create a new Input
1703 * @param {Object} config The config object
1706 Roo.bootstrap.Link = function(config){
1707 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1713 * The img click event for the img.
1714 * @param {Roo.EventObject} e
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1724 preventDefault: false,
1730 getAutoCreate : function()
1732 var html = this.html || '';
1734 if (this.fa !== false) {
1735 html = '<i class="fa fa-' + this.fa + '"></i>';
1740 // anchor's do not require html/href...
1741 if (this.anchor === false) {
1743 cfg.href = this.href || '#';
1745 cfg.name = this.anchor;
1746 if (this.html !== false || this.fa !== false) {
1749 if (this.href !== false) {
1750 cfg.href = this.href;
1754 if(this.alt !== false){
1759 if(this.target !== false) {
1760 cfg.target = this.target;
1766 initEvents: function() {
1768 if(!this.href || this.preventDefault){
1769 this.el.on('click', this.onClick, this);
1773 onClick : function(e)
1775 if(this.preventDefault){
1778 //Roo.log('img onclick');
1779 this.fireEvent('click', this, e);
1792 * @class Roo.bootstrap.Header
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Header class
1795 * @cfg {String} html content of header
1796 * @cfg {Number} level (1|2|3|4|5|6) default 1
1799 * Create a new Header
1800 * @param {Object} config The config object
1804 Roo.bootstrap.Header = function(config){
1805 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1816 getAutoCreate : function(){
1821 tag: 'h' + (1 *this.level),
1822 html: this.html || ''
1834 * Ext JS Library 1.1.1
1835 * Copyright(c) 2006-2007, Ext JS, LLC.
1837 * Originally Released Under LGPL - original licence link has changed is not relivant.
1840 * <script type="text/javascript">
1844 * @class Roo.bootstrap.MenuMgr
1845 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1848 Roo.bootstrap.MenuMgr = function(){
1849 var menus, active, groups = {}, attached = false, lastShow = new Date();
1851 // private - called when first menu is created
1854 active = new Roo.util.MixedCollection();
1855 Roo.get(document).addKeyListener(27, function(){
1856 if(active.length > 0){
1864 if(active && active.length > 0){
1865 var c = active.clone();
1875 if(active.length < 1){
1876 Roo.get(document).un("mouseup", onMouseDown);
1884 var last = active.last();
1885 lastShow = new Date();
1888 Roo.get(document).on("mouseup", onMouseDown);
1893 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894 m.parentMenu.activeChild = m;
1895 }else if(last && last.isVisible()){
1896 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1901 function onBeforeHide(m){
1903 m.activeChild.hide();
1905 if(m.autoHideTimer){
1906 clearTimeout(m.autoHideTimer);
1907 delete m.autoHideTimer;
1912 function onBeforeShow(m){
1913 var pm = m.parentMenu;
1914 if(!pm && !m.allowOtherMenus){
1916 }else if(pm && pm.activeChild && active != m){
1917 pm.activeChild.hide();
1921 // private this should really trigger on mouseup..
1922 function onMouseDown(e){
1923 Roo.log("on Mouse Up");
1925 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926 Roo.log("MenuManager hideAll");
1935 function onBeforeCheck(mi, state){
1937 var g = groups[mi.group];
1938 for(var i = 0, l = g.length; i < l; i++){
1940 g[i].setChecked(false);
1949 * Hides all menus that are currently visible
1951 hideAll : function(){
1956 register : function(menu){
1960 menus[menu.id] = menu;
1961 menu.on("beforehide", onBeforeHide);
1962 menu.on("hide", onHide);
1963 menu.on("beforeshow", onBeforeShow);
1964 menu.on("show", onShow);
1966 if(g && menu.events["checkchange"]){
1970 groups[g].push(menu);
1971 menu.on("checkchange", onCheck);
1976 * Returns a {@link Roo.menu.Menu} object
1977 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978 * be used to generate and return a new Menu instance.
1980 get : function(menu){
1981 if(typeof menu == "string"){ // menu id
1983 }else if(menu.events){ // menu instance
1986 /*else if(typeof menu.length == 'number'){ // array of menu items?
1987 return new Roo.bootstrap.Menu({items:menu});
1988 }else{ // otherwise, must be a config
1989 return new Roo.bootstrap.Menu(menu);
1996 unregister : function(menu){
1997 delete menus[menu.id];
1998 menu.un("beforehide", onBeforeHide);
1999 menu.un("hide", onHide);
2000 menu.un("beforeshow", onBeforeShow);
2001 menu.un("show", onShow);
2003 if(g && menu.events["checkchange"]){
2004 groups[g].remove(menu);
2005 menu.un("checkchange", onCheck);
2010 registerCheckable : function(menuItem){
2011 var g = menuItem.group;
2016 groups[g].push(menuItem);
2017 menuItem.on("beforecheckchange", onBeforeCheck);
2022 unregisterCheckable : function(menuItem){
2023 var g = menuItem.group;
2025 groups[g].remove(menuItem);
2026 menuItem.un("beforecheckchange", onBeforeCheck);
2038 * @class Roo.bootstrap.Menu
2039 * @extends Roo.bootstrap.Component
2040 * Bootstrap Menu class - container for MenuItems
2041 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042 * @cfg {bool} hidden if the menu should be hidden when rendered.
2043 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2044 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2048 * @param {Object} config The config object
2052 Roo.bootstrap.Menu = function(config){
2053 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054 if (this.registerMenu && this.type != 'treeview') {
2055 Roo.bootstrap.MenuMgr.register(this);
2062 * Fires before this menu is displayed
2063 * @param {Roo.menu.Menu} this
2068 * Fires before this menu is hidden
2069 * @param {Roo.menu.Menu} this
2074 * Fires after this menu is displayed
2075 * @param {Roo.menu.Menu} this
2080 * Fires after this menu is hidden
2081 * @param {Roo.menu.Menu} this
2086 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087 * @param {Roo.menu.Menu} this
2088 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089 * @param {Roo.EventObject} e
2094 * Fires when the mouse is hovering over this menu
2095 * @param {Roo.menu.Menu} this
2096 * @param {Roo.EventObject} e
2097 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2102 * Fires when the mouse exits this menu
2103 * @param {Roo.menu.Menu} this
2104 * @param {Roo.EventObject} e
2105 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2110 * Fires when a menu item contained in this menu is clicked
2111 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112 * @param {Roo.EventObject} e
2116 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2123 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2126 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128 registerMenu : true,
2130 menuItems :false, // stores the menu items..
2140 getChildContainer : function() {
2144 getAutoCreate : function(){
2146 //if (['right'].indexOf(this.align)!==-1) {
2147 // cfg.cn[1].cls += ' pull-right'
2153 cls : 'dropdown-menu' ,
2154 style : 'z-index:1000'
2158 if (this.type === 'submenu') {
2159 cfg.cls = 'submenu active';
2161 if (this.type === 'treeview') {
2162 cfg.cls = 'treeview-menu';
2167 initEvents : function() {
2169 // Roo.log("ADD event");
2170 // Roo.log(this.triggerEl.dom);
2172 this.triggerEl.on('click', this.onTriggerClick, this);
2174 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2177 if (this.triggerEl.hasClass('nav-item')) {
2178 // dropdown toggle on the 'a' in BS4?
2179 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181 this.triggerEl.addClass('dropdown-toggle');
2184 this.el.on('touchstart' , this.onTouch, this);
2186 this.el.on('click' , this.onClick, this);
2188 this.el.on("mouseover", this.onMouseOver, this);
2189 this.el.on("mouseout", this.onMouseOut, this);
2193 findTargetItem : function(e)
2195 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2199 //Roo.log(t); Roo.log(t.id);
2201 //Roo.log(this.menuitems);
2202 return this.menuitems.get(t.id);
2204 //return this.items.get(t.menuItemId);
2210 onTouch : function(e)
2212 Roo.log("menu.onTouch");
2213 //e.stopEvent(); this make the user popdown broken
2217 onClick : function(e)
2219 Roo.log("menu.onClick");
2221 var t = this.findTargetItem(e);
2222 if(!t || t.isContainer){
2227 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2228 if(t == this.activeItem && t.shouldDeactivate(e)){
2229 this.activeItem.deactivate();
2230 delete this.activeItem;
2234 this.setActiveItem(t, true);
2242 Roo.log('pass click event');
2246 this.fireEvent("click", this, t, e);
2250 if(!t.href.length || t.href == '#'){
2251 (function() { _this.hide(); }).defer(100);
2256 onMouseOver : function(e){
2257 var t = this.findTargetItem(e);
2260 // if(t.canActivate && !t.disabled){
2261 // this.setActiveItem(t, true);
2265 this.fireEvent("mouseover", this, e, t);
2267 isVisible : function(){
2268 return !this.hidden;
2270 onMouseOut : function(e){
2271 var t = this.findTargetItem(e);
2274 // if(t == this.activeItem && t.shouldDeactivate(e)){
2275 // this.activeItem.deactivate();
2276 // delete this.activeItem;
2279 this.fireEvent("mouseout", this, e, t);
2284 * Displays this menu relative to another element
2285 * @param {String/HTMLElement/Roo.Element} element The element to align to
2286 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287 * the element (defaults to this.defaultAlign)
2288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290 show : function(el, pos, parentMenu){
2291 this.parentMenu = parentMenu;
2295 this.fireEvent("beforeshow", this);
2296 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2299 * Displays this menu at a specific xy position
2300 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303 showAt : function(xy, parentMenu, /* private: */_e){
2304 this.parentMenu = parentMenu;
2309 this.fireEvent("beforeshow", this);
2310 //xy = this.el.adjustForConstraints(xy);
2314 this.hideMenuItems();
2315 this.hidden = false;
2316 this.triggerEl.addClass('open');
2317 this.el.addClass('show');
2319 // reassign x when hitting right
2320 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2324 // reassign y when hitting bottom
2325 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2329 // but the list may align on trigger left or trigger top... should it be a properity?
2331 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2336 this.fireEvent("show", this);
2342 this.doFocus.defer(50, this);
2346 doFocus : function(){
2348 this.focusEl.focus();
2353 * Hides this menu and optionally all parent menus
2354 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356 hide : function(deep)
2359 this.hideMenuItems();
2360 if(this.el && this.isVisible()){
2361 this.fireEvent("beforehide", this);
2362 if(this.activeItem){
2363 this.activeItem.deactivate();
2364 this.activeItem = null;
2366 this.triggerEl.removeClass('open');;
2367 this.el.removeClass('show');
2369 this.fireEvent("hide", this);
2371 if(deep === true && this.parentMenu){
2372 this.parentMenu.hide(true);
2376 onTriggerClick : function(e)
2378 Roo.log('trigger click');
2380 var target = e.getTarget();
2382 Roo.log(target.nodeName.toLowerCase());
2384 if(target.nodeName.toLowerCase() === 'i'){
2390 onTriggerPress : function(e)
2392 Roo.log('trigger press');
2393 //Roo.log(e.getTarget());
2394 // Roo.log(this.triggerEl.dom);
2396 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397 var pel = Roo.get(e.getTarget());
2398 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399 Roo.log('is treeview or dropdown?');
2403 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2407 if (this.isVisible()) {
2412 this.show(this.triggerEl, false, false);
2415 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2422 hideMenuItems : function()
2424 Roo.log("hide Menu Items");
2428 //$(backdrop).remove()
2429 this.el.select('.open',true).each(function(aa) {
2431 aa.removeClass('open');
2432 //var parent = getParent($(this))
2433 //var relatedTarget = { relatedTarget: this }
2435 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436 //if (e.isDefaultPrevented()) return
2437 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2440 addxtypeChild : function (tree, cntr) {
2441 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443 this.menuitems.add(comp);
2455 this.getEl().dom.innerHTML = '';
2456 this.menuitems.clear();
2470 * @class Roo.bootstrap.MenuItem
2471 * @extends Roo.bootstrap.Component
2472 * Bootstrap MenuItem class
2473 * @cfg {String} html the menu label
2474 * @cfg {String} href the link
2475 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477 * @cfg {Boolean} active used on sidebars to highlight active itesm
2478 * @cfg {String} fa favicon to show on left of menu item.
2479 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2483 * Create a new MenuItem
2484 * @param {Object} config The config object
2488 Roo.bootstrap.MenuItem = function(config){
2489 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494 * The raw click event for the entire grid.
2495 * @param {Roo.bootstrap.MenuItem} this
2496 * @param {Roo.EventObject} e
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2506 preventDefault: false,
2507 isContainer : false,
2511 getAutoCreate : function(){
2513 if(this.isContainer){
2516 cls: 'dropdown-menu-item dropdown-item'
2530 if (this.fa !== false) {
2533 cls : 'fa fa-' + this.fa
2542 cls: 'dropdown-menu-item dropdown-item',
2545 if (this.parent().type == 'treeview') {
2546 cfg.cls = 'treeview-menu';
2549 cfg.cls += ' active';
2554 anc.href = this.href || cfg.cn[0].href ;
2555 ctag.html = this.html || cfg.cn[0].html ;
2559 initEvents: function()
2561 if (this.parent().type == 'treeview') {
2562 this.el.select('a').on('click', this.onClick, this);
2566 this.menu.parentType = this.xtype;
2567 this.menu.triggerEl = this.el;
2568 this.menu = this.addxtype(Roo.apply({}, this.menu));
2572 onClick : function(e)
2574 Roo.log('item on click ');
2576 if(this.preventDefault){
2579 //this.parent().hideMenuItems();
2581 this.fireEvent('click', this, e);
2600 * @class Roo.bootstrap.MenuSeparator
2601 * @extends Roo.bootstrap.Component
2602 * Bootstrap MenuSeparator class
2605 * Create a new MenuItem
2606 * @param {Object} config The config object
2610 Roo.bootstrap.MenuSeparator = function(config){
2611 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2616 getAutoCreate : function(){
2635 * @class Roo.bootstrap.Modal
2636 * @extends Roo.bootstrap.Component
2637 * Bootstrap Modal class
2638 * @cfg {String} title Title of dialog
2639 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2641 * @cfg {Boolean} specificTitle default false
2642 * @cfg {Array} buttons Array of buttons or standard button set..
2643 * @cfg {String} buttonPosition (left|right|center) default right
2644 * @cfg {Boolean} animate default true
2645 * @cfg {Boolean} allow_close default true
2646 * @cfg {Boolean} fitwindow default false
2647 * @cfg {String} size (sm|lg) default empty
2648 * @cfg {Number} max_width set the max width of modal
2652 * Create a new Modal Dialog
2653 * @param {Object} config The config object
2656 Roo.bootstrap.Modal = function(config){
2657 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2662 * The raw btnclick event for the button
2663 * @param {Roo.EventObject} e
2668 * Fire when dialog resize
2669 * @param {Roo.bootstrap.Modal} this
2670 * @param {Roo.EventObject} e
2674 this.buttons = this.buttons || [];
2677 this.tmpl = Roo.factory(this.tmpl);
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2684 title : 'test dialog',
2694 specificTitle: false,
2696 buttonPosition: 'right',
2719 onRender : function(ct, position)
2721 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724 var cfg = Roo.apply({}, this.getAutoCreate());
2727 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729 //if (!cfg.name.length) {
2733 cfg.cls += ' ' + this.cls;
2736 cfg.style = this.style;
2738 this.el = Roo.get(document.body).createChild(cfg, position);
2740 //var type = this.el.dom.type;
2743 if(this.tabIndex !== undefined){
2744 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747 this.dialogEl = this.el.select('.modal-dialog',true).first();
2748 this.bodyEl = this.el.select('.modal-body',true).first();
2749 this.closeEl = this.el.select('.modal-header .close', true).first();
2750 this.headerEl = this.el.select('.modal-header',true).first();
2751 this.titleEl = this.el.select('.modal-title',true).first();
2752 this.footerEl = this.el.select('.modal-footer',true).first();
2754 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756 //this.el.addClass("x-dlg-modal");
2758 if (this.buttons.length) {
2759 Roo.each(this.buttons, function(bb) {
2760 var b = Roo.apply({}, bb);
2761 b.xns = b.xns || Roo.bootstrap;
2762 b.xtype = b.xtype || 'Button';
2763 if (typeof(b.listeners) == 'undefined') {
2764 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2767 var btn = Roo.factory(b);
2769 btn.render(this.el.select('.modal-footer div').first());
2773 // render the children.
2776 if(typeof(this.items) != 'undefined'){
2777 var items = this.items;
2780 for(var i =0;i < items.length;i++) {
2781 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2785 this.items = nitems;
2787 // where are these used - they used to be body/close/footer
2791 //this.el.addClass([this.fieldClass, this.cls]);
2795 getAutoCreate : function()
2799 html : this.html || ''
2804 cls : 'modal-title',
2808 if(this.specificTitle){
2814 if (this.allow_close && Roo.bootstrap.version == 3) {
2824 if (this.allow_close && Roo.bootstrap.version == 4) {
2834 if(this.size.length){
2835 size = 'modal-' + this.size;
2842 cls: "modal-dialog " + size,
2845 cls : "modal-content",
2848 cls : 'modal-header',
2853 cls : 'modal-footer',
2857 cls: 'btn-' + this.buttonPosition
2874 modal.cls += ' fade';
2880 getChildContainer : function() {
2885 getButtonContainer : function() {
2886 return this.el.select('.modal-footer div',true).first();
2889 initEvents : function()
2891 if (this.allow_close) {
2892 this.closeEl.on('click', this.hide, this);
2894 Roo.EventManager.onWindowResize(this.resize, this, true);
2901 this.maskEl.setSize(
2902 Roo.lib.Dom.getViewWidth(true),
2903 Roo.lib.Dom.getViewHeight(true)
2906 if (this.fitwindow) {
2908 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2914 if(this.max_width !== 0) {
2916 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919 this.setSize(w, this.height);
2923 if(this.max_height) {
2924 this.setSize(w,Math.min(
2926 Roo.lib.Dom.getViewportHeight(true) - 60
2932 if(!this.fit_content) {
2933 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2937 this.setSize(w, Math.min(
2939 this.headerEl.getHeight() +
2940 this.footerEl.getHeight() +
2941 this.getChildHeight(this.bodyEl.dom.childNodes),
2942 Roo.lib.Dom.getViewportHeight(true) - 60)
2948 setSize : function(w,h)
2959 if (!this.rendered) {
2963 //this.el.setStyle('display', 'block');
2964 this.el.removeClass('hideing');
2965 this.el.dom.style.display='block';
2967 Roo.get(document.body).addClass('modal-open');
2969 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2972 this.el.addClass('show');
2973 this.el.addClass('in');
2976 this.el.addClass('show');
2977 this.el.addClass('in');
2980 // not sure how we can show data in here..
2982 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985 Roo.get(document.body).addClass("x-body-masked");
2987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2988 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989 this.maskEl.dom.style.display = 'block';
2990 this.maskEl.addClass('show');
2995 this.fireEvent('show', this);
2997 // set zindex here - otherwise it appears to be ignored...
2998 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001 this.items.forEach( function(e) {
3002 e.layout ? e.layout() : false;
3010 if(this.fireEvent("beforehide", this) !== false){
3012 this.maskEl.removeClass('show');
3014 this.maskEl.dom.style.display = '';
3015 Roo.get(document.body).removeClass("x-body-masked");
3016 this.el.removeClass('in');
3017 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019 if(this.animate){ // why
3020 this.el.addClass('hideing');
3021 this.el.removeClass('show');
3023 if (!this.el.hasClass('hideing')) {
3024 return; // it's been shown again...
3027 this.el.dom.style.display='';
3029 Roo.get(document.body).removeClass('modal-open');
3030 this.el.removeClass('hideing');
3034 this.el.removeClass('show');
3035 this.el.dom.style.display='';
3036 Roo.get(document.body).removeClass('modal-open');
3039 this.fireEvent('hide', this);
3042 isVisible : function()
3045 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3049 addButton : function(str, cb)
3053 var b = Roo.apply({}, { html : str } );
3054 b.xns = b.xns || Roo.bootstrap;
3055 b.xtype = b.xtype || 'Button';
3056 if (typeof(b.listeners) == 'undefined') {
3057 b.listeners = { click : cb.createDelegate(this) };
3060 var btn = Roo.factory(b);
3062 btn.render(this.el.select('.modal-footer div').first());
3068 setDefaultButton : function(btn)
3070 //this.el.select('.modal-footer').()
3074 resizeTo: function(w,h)
3078 this.dialogEl.setWidth(w);
3079 if (this.diff === false) {
3080 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083 this.bodyEl.setHeight(h - this.diff);
3085 this.fireEvent('resize', this);
3088 setContentSize : function(w, h)
3092 onButtonClick: function(btn,e)
3095 this.fireEvent('btnclick', btn.name, e);
3098 * Set the title of the Dialog
3099 * @param {String} str new Title
3101 setTitle: function(str) {
3102 this.titleEl.dom.innerHTML = str;
3105 * Set the body of the Dialog
3106 * @param {String} str new Title
3108 setBody: function(str) {
3109 this.bodyEl.dom.innerHTML = str;
3112 * Set the body of the Dialog using the template
3113 * @param {Obj} data - apply this data to the template and replace the body contents.
3115 applyBody: function(obj)
3118 Roo.log("Error - using apply Body without a template");
3121 this.tmpl.overwrite(this.bodyEl, obj);
3124 getChildHeight : function(child_nodes)
3128 child_nodes.length == 0
3133 var child_height = 0;
3135 for(var i = 0; i < child_nodes.length; i++) {
3138 * for modal with tabs...
3139 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141 var layout_childs = child_nodes[i].childNodes;
3143 for(var j = 0; j < layout_childs.length; j++) {
3145 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147 var layout_body_childs = layout_childs[j].childNodes;
3149 for(var k = 0; k < layout_body_childs.length; k++) {
3151 if(layout_body_childs[k].classList.contains('navbar')) {
3152 child_height += layout_body_childs[k].offsetHeight;
3156 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3178 child_height += child_nodes[i].offsetHeight;
3179 // Roo.log(child_nodes[i].offsetHeight);
3182 return child_height;
3188 Roo.apply(Roo.bootstrap.Modal, {
3190 * Button config that displays a single OK button
3199 * Button config that displays Yes and No buttons
3215 * Button config that displays OK and Cancel buttons
3230 * Button config that displays Yes, No and Cancel buttons
3254 * messagebox - can be used as a replace
3258 * @class Roo.MessageBox
3259 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268 // process text value...
3272 // Show a dialog using config options:
3274 title:'Save Changes?',
3275 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276 buttons: Roo.Msg.YESNOCANCEL,
3283 Roo.bootstrap.MessageBox = function(){
3284 var dlg, opt, mask, waitTimer;
3285 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286 var buttons, activeTextEl, bwidth;
3290 var handleButton = function(button){
3292 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3296 var handleHide = function(){
3298 dlg.el.removeClass(opt.cls);
3301 // Roo.TaskMgr.stop(waitTimer);
3302 // waitTimer = null;
3307 var updateButtons = function(b){
3310 buttons["ok"].hide();
3311 buttons["cancel"].hide();
3312 buttons["yes"].hide();
3313 buttons["no"].hide();
3314 //dlg.footer.dom.style.display = 'none';
3317 dlg.footerEl.dom.style.display = '';
3318 for(var k in buttons){
3319 if(typeof buttons[k] != "function"){
3322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323 width += buttons[k].el.getWidth()+15;
3333 var handleEsc = function(d, k, e){
3334 if(opt && opt.closable !== false){
3344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345 * @return {Roo.BasicDialog} The BasicDialog element
3347 getDialog : function(){
3349 dlg = new Roo.bootstrap.Modal( {
3352 //constraintoviewport:false,
3354 //collapsible : false,
3359 //buttonAlign:"center",
3360 closeClick : function(){
3361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364 handleButton("cancel");
3369 dlg.on("hide", handleHide);
3371 //dlg.addKeyListener(27, handleEsc);
3373 this.buttons = buttons;
3374 var bt = this.buttonText;
3375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380 bodyEl = dlg.bodyEl.createChild({
3382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383 '<textarea class="roo-mb-textarea"></textarea>' +
3384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3386 msgEl = bodyEl.dom.firstChild;
3387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388 textboxEl.enableDisplayMode();
3389 textboxEl.addKeyListener([10,13], function(){
3390 if(dlg.isVisible() && opt && opt.buttons){
3393 }else if(opt.buttons.yes){
3394 handleButton("yes");
3398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399 textareaEl.enableDisplayMode();
3400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401 progressEl.enableDisplayMode();
3403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404 var pf = progressEl.dom.firstChild;
3406 pp = Roo.get(pf.firstChild);
3407 pp.setHeight(pf.offsetHeight);
3415 * Updates the message box body text
3416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417 * the XHTML-compliant non-breaking space character '&#160;')
3418 * @return {Roo.MessageBox} This message box
3420 updateText : function(text)
3422 if(!dlg.isVisible() && !opt.width){
3423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426 msgEl.innerHTML = text || ' ';
3428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431 Math.min(opt.width || cw , this.maxWidth),
3432 Math.max(opt.minWidth || this.minWidth, bwidth)
3435 activeTextEl.setWidth(w);
3437 if(dlg.isVisible()){
3438 dlg.fixedcenter = false;
3440 // to big, make it scroll. = But as usual stupid IE does not support
3443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447 bodyEl.dom.style.height = '';
3448 bodyEl.dom.style.overflowY = '';
3451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453 bodyEl.dom.style.overflowX = '';
3456 dlg.setContentSize(w, bodyEl.getHeight());
3457 if(dlg.isVisible()){
3458 dlg.fixedcenter = true;
3464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468 * @return {Roo.MessageBox} This message box
3470 updateProgress : function(value, text){
3472 this.updateText(text);
3475 if (pp) { // weird bug on my firefox - for some reason this is not defined
3476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3483 * Returns true if the message box is currently displayed
3484 * @return {Boolean} True if the message box is visible, else false
3486 isVisible : function(){
3487 return dlg && dlg.isVisible();
3491 * Hides the message box if it is displayed
3494 if(this.isVisible()){
3500 * Displays a new message box, or reinitializes an existing message box, based on the config options
3501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502 * The following config object properties are supported:
3504 Property Type Description
3505 ---------- --------------- ------------------------------------------------------------------------------------
3506 animEl String/Element An id or Element from which the message box should animate as it opens and
3507 closes (defaults to undefined)
3508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable Boolean False to hide the top-right close button (defaults to true). Note that
3511 progress and wait dialogs will ignore this property and always hide the
3512 close button as they can only be closed programmatically.
3513 cls String A custom CSS class to apply to the message box element
3514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3515 displayed (defaults to 75)
3516 fn Function A callback function to execute after closing the dialog. The arguments to the
3517 function will be btn (the name of the button that was clicked, if applicable,
3518 e.g. "ok"), and text (the value of the active text field, if applicable).
3519 Progress and wait dialogs will ignore this option since they do not respond to
3520 user actions and can only be closed programmatically, so any required function
3521 should be called by the same code after it closes the dialog.
3522 icon String A CSS class that provides a background image to be used as an icon for
3523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3526 modal Boolean False to allow user interaction with the page while the message box is
3527 displayed (defaults to true)
3528 msg String A string that will replace the existing message box body text (defaults
3529 to the XHTML-compliant non-breaking space character ' ')
3530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3531 progress Boolean True to display a progress bar (defaults to false)
3532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3535 title String The title text
3536 value String The string value to set into the active textbox element if displayed
3537 wait Boolean True to display a progress bar (defaults to false)
3538 width Number The width of the dialog in pixels
3545 msg: 'Please enter your address:',
3547 buttons: Roo.MessageBox.OKCANCEL,
3550 animEl: 'addAddressBtn'
3553 * @param {Object} config Configuration options
3554 * @return {Roo.MessageBox} This message box
3556 show : function(options)
3559 // this causes nightmares if you show one dialog after another
3560 // especially on callbacks..
3562 if(this.isVisible()){
3565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3567 Roo.log("New Dialog Message:" + options.msg )
3568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572 var d = this.getDialog();
3574 d.setTitle(opt.title || " ");
3575 d.closeEl.setDisplayed(opt.closable !== false);
3576 activeTextEl = textboxEl;
3577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3582 textareaEl.setHeight(typeof opt.multiline == "number" ?
3583 opt.multiline : this.defaultTextHeight);
3584 activeTextEl = textareaEl;
3593 progressEl.setDisplayed(opt.progress === true);
3594 this.updateProgress(0);
3595 activeTextEl.dom.value = opt.value || "";
3597 dlg.setDefaultButton(activeTextEl);
3599 var bs = opt.buttons;
3603 }else if(bs && bs.yes){
3604 db = buttons["yes"];
3606 dlg.setDefaultButton(db);
3608 bwidth = updateButtons(opt.buttons);
3609 this.updateText(opt.msg);
3611 d.el.addClass(opt.cls);
3613 d.proxyDrag = opt.proxyDrag === true;
3614 d.modal = opt.modal !== false;
3615 d.mask = opt.modal !== false ? mask : false;
3617 // force it to the end of the z-index stack so it gets a cursor in FF
3618 document.body.appendChild(dlg.el.dom);
3619 d.animateTarget = null;
3620 d.show(options.animEl);
3626 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3627 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628 * and closing the message box when the process is complete.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @return {Roo.MessageBox} This message box
3633 progress : function(title, msg){
3640 minWidth: this.minProgressWidth,
3647 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648 * If a callback function is passed it will be called after the user clicks the button, and the
3649 * id of the button that was clicked will be passed as the only parameter to the callback
3650 * (could also be the top-right close button).
3651 * @param {String} title The title bar text
3652 * @param {String} msg The message box body text
3653 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654 * @param {Object} scope (optional) The scope of the callback function
3655 * @return {Roo.MessageBox} This message box
3657 alert : function(title, msg, fn, scope)
3672 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3673 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674 * You are responsible for closing the message box when the process is complete.
3675 * @param {String} msg The message box body text
3676 * @param {String} title (optional) The title bar text
3677 * @return {Roo.MessageBox} This message box
3679 wait : function(msg, title){
3690 waitTimer = Roo.TaskMgr.start({
3692 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3700 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703 * @param {String} title The title bar text
3704 * @param {String} msg The message box body text
3705 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706 * @param {Object} scope (optional) The scope of the callback function
3707 * @return {Roo.MessageBox} This message box
3709 confirm : function(title, msg, fn, scope){
3713 buttons: this.YESNO,
3722 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3724 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725 * (could also be the top-right close button) and the text that was entered will be passed as the two
3726 * parameters to the callback.
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733 * @return {Roo.MessageBox} This message box
3735 prompt : function(title, msg, fn, scope, multiline){
3739 buttons: this.OKCANCEL,
3744 multiline: multiline,
3751 * Button config that displays a single OK button
3756 * Button config that displays Yes and No buttons
3759 YESNO : {yes:true, no:true},
3761 * Button config that displays OK and Cancel buttons
3764 OKCANCEL : {ok:true, cancel:true},
3766 * Button config that displays Yes, No and Cancel buttons
3769 YESNOCANCEL : {yes:true, no:true, cancel:true},
3772 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775 defaultTextHeight : 75,
3777 * The maximum width in pixels of the message box (defaults to 600)
3782 * The minimum width in pixels of the message box (defaults to 100)
3787 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3788 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791 minProgressWidth : 250,
3793 * An object containing the default button text strings that can be overriden for localized language support.
3794 * Supported properties are: ok, cancel, yes and no.
3795 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3808 * Shorthand for {@link Roo.MessageBox}
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3820 * @class Roo.bootstrap.Navbar
3821 * @extends Roo.bootstrap.Component
3822 * Bootstrap Navbar class
3825 * Create a new Navbar
3826 * @param {Object} config The config object
3830 Roo.bootstrap.Navbar = function(config){
3831 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3835 * @event beforetoggle
3836 * Fire before toggle the menu
3837 * @param {Roo.EventObject} e
3839 "beforetoggle" : true
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3852 getAutoCreate : function(){
3855 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3859 initEvents :function ()
3861 //Roo.log(this.el.select('.navbar-toggle',true));
3862 this.el.select('.navbar-toggle',true).on('click', function() {
3863 if(this.fireEvent('beforetoggle', this) !== false){
3864 var ce = this.el.select('.navbar-collapse',true).first();
3865 ce.toggleClass('in'); // old...
3866 if (ce.hasClass('collapse')) {
3867 ce.removeClass('collapse');
3868 ce.addClass('collapsing');
3871 ce.addClass('collapse');
3882 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3884 var size = this.el.getSize();
3885 this.maskEl.setSize(size.width, size.height);
3886 this.maskEl.enableDisplayMode("block");
3895 getChildContainer : function()
3897 if (this.el.select('.collapse').getCount()) {
3898 return this.el.select('.collapse',true).first();
3931 * @class Roo.bootstrap.NavSimplebar
3932 * @extends Roo.bootstrap.Navbar
3933 * Bootstrap Sidebar class
3935 * @cfg {Boolean} inverse is inverted color
3937 * @cfg {String} type (nav | pills | tabs)
3938 * @cfg {Boolean} arrangement stacked | justified
3939 * @cfg {String} align (left | right) alignment
3941 * @cfg {Boolean} main (true|false) main nav bar? default false
3942 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3944 * @cfg {String} tag (header|footer|nav|div) default is nav
3946 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavSimplebar = function(config){
3956 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3959 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3975 getAutoCreate : function(){
3979 tag : this.tag || 'div',
3980 cls : 'navbar navbar-expand-lg'
3982 if (['light','white'].indexOf(this.weight) > -1) {
3983 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3985 cfg.cls += ' bg-' + this.weight;
3997 this.type = this.type || 'nav';
3998 if (['tabs','pills'].indexOf(this.type)!==-1) {
3999 cfg.cn[0].cls += ' nav-' + this.type
4003 if (this.type!=='nav') {
4004 Roo.log('nav type must be nav/tabs/pills')
4006 cfg.cn[0].cls += ' navbar-nav'
4012 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4013 cfg.cn[0].cls += ' nav-' + this.arrangement;
4017 if (this.align === 'right') {
4018 cfg.cn[0].cls += ' navbar-right';
4022 cfg.cls += ' navbar-inverse';
4046 * navbar-expand-md fixed-top
4050 * @class Roo.bootstrap.NavHeaderbar
4051 * @extends Roo.bootstrap.NavSimplebar
4052 * Bootstrap Sidebar class
4054 * @cfg {String} brand what is brand
4055 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4056 * @cfg {String} brand_href href of the brand
4057 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4058 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4059 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4060 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4063 * Create a new Sidebar
4064 * @param {Object} config The config object
4068 Roo.bootstrap.NavHeaderbar = function(config){
4069 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4073 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4080 desktopCenter : false,
4083 getAutoCreate : function(){
4086 tag: this.nav || 'nav',
4087 cls: 'navbar navbar-expand-md',
4093 if (this.desktopCenter) {
4094 cn.push({cls : 'container', cn : []});
4102 cls: 'navbar-toggle navbar-toggler',
4103 'data-toggle': 'collapse',
4108 html: 'Toggle navigation'
4112 cls: 'icon-bar navbar-toggler-icon'
4125 cn.push( Roo.bootstrap.version == 4 ? btn : {
4127 cls: 'navbar-header',
4136 cls: 'collapse navbar-collapse',
4140 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4142 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4143 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4145 // tag can override this..
4147 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4150 if (this.brand !== '') {
4151 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4152 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4154 href: this.brand_href ? this.brand_href : '#',
4155 cls: 'navbar-brand',
4163 cfg.cls += ' main-nav';
4171 getHeaderChildContainer : function()
4173 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4174 return this.el.select('.navbar-header',true).first();
4177 return this.getChildContainer();
4181 initEvents : function()
4183 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4185 if (this.autohide) {
4190 Roo.get(document).on('scroll',function(e) {
4191 var ns = Roo.get(document).getScroll().top;
4192 var os = prevScroll;
4196 ft.removeClass('slideDown');
4197 ft.addClass('slideUp');
4200 ft.removeClass('slideUp');
4201 ft.addClass('slideDown');
4222 * @class Roo.bootstrap.NavSidebar
4223 * @extends Roo.bootstrap.Navbar
4224 * Bootstrap Sidebar class
4227 * Create a new Sidebar
4228 * @param {Object} config The config object
4232 Roo.bootstrap.NavSidebar = function(config){
4233 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4238 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4240 getAutoCreate : function(){
4245 cls: 'sidebar sidebar-nav'
4267 * @class Roo.bootstrap.NavGroup
4268 * @extends Roo.bootstrap.Component
4269 * Bootstrap NavGroup class
4270 * @cfg {String} align (left|right)
4271 * @cfg {Boolean} inverse
4272 * @cfg {String} type (nav|pills|tab) default nav
4273 * @cfg {String} navId - reference Id for navbar.
4277 * Create a new nav group
4278 * @param {Object} config The config object
4281 Roo.bootstrap.NavGroup = function(config){
4282 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4285 Roo.bootstrap.NavGroup.register(this);
4289 * Fires when the active item changes
4290 * @param {Roo.bootstrap.NavGroup} this
4291 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4292 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4299 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4310 getAutoCreate : function()
4312 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4319 if (['tabs','pills'].indexOf(this.type)!==-1) {
4320 cfg.cls += ' nav-' + this.type
4322 if (this.type!=='nav') {
4323 Roo.log('nav type must be nav/tabs/pills')
4325 cfg.cls += ' navbar-nav'
4328 if (this.parent() && this.parent().sidebar) {
4331 cls: 'dashboard-menu sidebar-menu'
4337 if (this.form === true) {
4343 if (this.align === 'right') {
4344 cfg.cls += ' navbar-right ml-md-auto';
4346 cfg.cls += ' navbar-left';
4350 if (this.align === 'right') {
4351 cfg.cls += ' navbar-right ml-md-auto';
4353 cfg.cls += ' mr-auto';
4357 cfg.cls += ' navbar-inverse';
4365 * sets the active Navigation item
4366 * @param {Roo.bootstrap.NavItem} the new current navitem
4368 setActiveItem : function(item)
4371 Roo.each(this.navItems, function(v){
4376 v.setActive(false, true);
4383 item.setActive(true, true);
4384 this.fireEvent('changed', this, item, prev);
4389 * gets the active Navigation item
4390 * @return {Roo.bootstrap.NavItem} the current navitem
4392 getActive : function()
4396 Roo.each(this.navItems, function(v){
4407 indexOfNav : function()
4411 Roo.each(this.navItems, function(v,i){
4422 * adds a Navigation item
4423 * @param {Roo.bootstrap.NavItem} the navitem to add
4425 addItem : function(cfg)
4427 var cn = new Roo.bootstrap.NavItem(cfg);
4429 cn.parentId = this.id;
4430 cn.onRender(this.el, null);
4434 * register a Navigation item
4435 * @param {Roo.bootstrap.NavItem} the navitem to add
4437 register : function(item)
4439 this.navItems.push( item);
4440 item.navId = this.navId;
4445 * clear all the Navigation item
4448 clearAll : function()
4451 this.el.dom.innerHTML = '';
4454 getNavItem: function(tabId)
4457 Roo.each(this.navItems, function(e) {
4458 if (e.tabId == tabId) {
4468 setActiveNext : function()
4470 var i = this.indexOfNav(this.getActive());
4471 if (i > this.navItems.length) {
4474 this.setActiveItem(this.navItems[i+1]);
4476 setActivePrev : function()
4478 var i = this.indexOfNav(this.getActive());
4482 this.setActiveItem(this.navItems[i-1]);
4484 clearWasActive : function(except) {
4485 Roo.each(this.navItems, function(e) {
4486 if (e.tabId != except.tabId && e.was_active) {
4487 e.was_active = false;
4494 getWasActive : function ()
4497 Roo.each(this.navItems, function(e) {
4512 Roo.apply(Roo.bootstrap.NavGroup, {
4516 * register a Navigation Group
4517 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4519 register : function(navgrp)
4521 this.groups[navgrp.navId] = navgrp;
4525 * fetch a Navigation Group based on the navigation ID
4526 * @param {string} the navgroup to add
4527 * @returns {Roo.bootstrap.NavGroup} the navgroup
4529 get: function(navId) {
4530 if (typeof(this.groups[navId]) == 'undefined') {
4532 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4534 return this.groups[navId] ;
4549 * @class Roo.bootstrap.NavItem
4550 * @extends Roo.bootstrap.Component
4551 * Bootstrap Navbar.NavItem class
4552 * @cfg {String} href link to
4553 * @cfg {String} html content of button
4554 * @cfg {String} badge text inside badge
4555 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4556 * @cfg {String} glyphicon DEPRICATED - use fa
4557 * @cfg {String} icon DEPRICATED - use fa
4558 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4559 * @cfg {Boolean} active Is item active
4560 * @cfg {Boolean} disabled Is item disabled
4562 * @cfg {Boolean} preventDefault (true | false) default false
4563 * @cfg {String} tabId the tab that this item activates.
4564 * @cfg {String} tagtype (a|span) render as a href or span?
4565 * @cfg {Boolean} animateRef (true|false) link to element default false
4568 * Create a new Navbar Item
4569 * @param {Object} config The config object
4571 Roo.bootstrap.NavItem = function(config){
4572 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4577 * The raw click event for the entire grid.
4578 * @param {Roo.EventObject} e
4583 * Fires when the active item active state changes
4584 * @param {Roo.bootstrap.NavItem} this
4585 * @param {boolean} state the new state
4591 * Fires when scroll to element
4592 * @param {Roo.bootstrap.NavItem} this
4593 * @param {Object} options
4594 * @param {Roo.EventObject} e
4602 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4611 preventDefault : false,
4618 getAutoCreate : function(){
4627 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4629 if (this.disabled) {
4630 cfg.cls += ' disabled';
4633 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4637 href : this.href || "#",
4638 html: this.html || ''
4641 if (this.tagtype == 'a') {
4642 cfg.cn[0].cls = 'nav-link';
4645 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4648 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4650 if(this.glyphicon) {
4651 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4656 cfg.cn[0].html += " <span class='caret'></span>";
4660 if (this.badge !== '') {
4662 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4670 initEvents: function()
4672 if (typeof (this.menu) != 'undefined') {
4673 this.menu.parentType = this.xtype;
4674 this.menu.triggerEl = this.el;
4675 this.menu = this.addxtype(Roo.apply({}, this.menu));
4678 this.el.select('a',true).on('click', this.onClick, this);
4680 if(this.tagtype == 'span'){
4681 this.el.select('span',true).on('click', this.onClick, this);
4684 // at this point parent should be available..
4685 this.parent().register(this);
4688 onClick : function(e)
4690 if (e.getTarget('.dropdown-menu-item')) {
4691 // did you click on a menu itemm.... - then don't trigger onclick..
4696 this.preventDefault ||
4699 Roo.log("NavItem - prevent Default?");
4703 if (this.disabled) {
4707 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4708 if (tg && tg.transition) {
4709 Roo.log("waiting for the transitionend");
4715 //Roo.log("fire event clicked");
4716 if(this.fireEvent('click', this, e) === false){
4720 if(this.tagtype == 'span'){
4724 //Roo.log(this.href);
4725 var ael = this.el.select('a',true).first();
4728 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4729 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4730 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4731 return; // ignore... - it's a 'hash' to another page.
4733 Roo.log("NavItem - prevent Default?");
4735 this.scrollToElement(e);
4739 var p = this.parent();
4741 if (['tabs','pills'].indexOf(p.type)!==-1) {
4742 if (typeof(p.setActiveItem) !== 'undefined') {
4743 p.setActiveItem(this);
4747 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4748 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4749 // remove the collapsed menu expand...
4750 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4754 isActive: function () {
4757 setActive : function(state, fire, is_was_active)
4759 if (this.active && !state && this.navId) {
4760 this.was_active = true;
4761 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4763 nv.clearWasActive(this);
4767 this.active = state;
4770 this.el.removeClass('active');
4771 } else if (!this.el.hasClass('active')) {
4772 this.el.addClass('active');
4775 this.fireEvent('changed', this, state);
4778 // show a panel if it's registered and related..
4780 if (!this.navId || !this.tabId || !state || is_was_active) {
4784 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4788 var pan = tg.getPanelByName(this.tabId);
4792 // if we can not flip to new panel - go back to old nav highlight..
4793 if (false == tg.showPanel(pan)) {
4794 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4796 var onav = nv.getWasActive();
4798 onav.setActive(true, false, true);
4807 // this should not be here...
4808 setDisabled : function(state)
4810 this.disabled = state;
4812 this.el.removeClass('disabled');
4813 } else if (!this.el.hasClass('disabled')) {
4814 this.el.addClass('disabled');
4820 * Fetch the element to display the tooltip on.
4821 * @return {Roo.Element} defaults to this.el
4823 tooltipEl : function()
4825 return this.el.select('' + this.tagtype + '', true).first();
4828 scrollToElement : function(e)
4830 var c = document.body;
4833 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4835 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4836 c = document.documentElement;
4839 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4845 var o = target.calcOffsetsTo(c);
4852 this.fireEvent('scrollto', this, options, e);
4854 Roo.get(c).scrollTo('top', options.value, true);
4867 * <span> icon </span>
4868 * <span> text </span>
4869 * <span>badge </span>
4873 * @class Roo.bootstrap.NavSidebarItem
4874 * @extends Roo.bootstrap.NavItem
4875 * Bootstrap Navbar.NavSidebarItem class
4876 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4877 * {Boolean} open is the menu open
4878 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4879 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4880 * {String} buttonSize (sm|md|lg)the extra classes for the button
4881 * {Boolean} showArrow show arrow next to the text (default true)
4883 * Create a new Navbar Button
4884 * @param {Object} config The config object
4886 Roo.bootstrap.NavSidebarItem = function(config){
4887 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4892 * The raw click event for the entire grid.
4893 * @param {Roo.EventObject} e
4898 * Fires when the active item active state changes
4899 * @param {Roo.bootstrap.NavSidebarItem} this
4900 * @param {boolean} state the new state
4908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4910 badgeWeight : 'default',
4916 buttonWeight : 'default',
4922 getAutoCreate : function(){
4927 href : this.href || '#',
4933 if(this.buttonView){
4936 href : this.href || '#',
4937 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4950 cfg.cls += ' active';
4953 if (this.disabled) {
4954 cfg.cls += ' disabled';
4957 cfg.cls += ' open x-open';
4960 if (this.glyphicon || this.icon) {
4961 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4962 a.cn.push({ tag : 'i', cls : c }) ;
4965 if(!this.buttonView){
4968 html : this.html || ''
4975 if (this.badge !== '') {
4976 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4982 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4985 a.cls += ' dropdown-toggle treeview' ;
4991 initEvents : function()
4993 if (typeof (this.menu) != 'undefined') {
4994 this.menu.parentType = this.xtype;
4995 this.menu.triggerEl = this.el;
4996 this.menu = this.addxtype(Roo.apply({}, this.menu));
4999 this.el.on('click', this.onClick, this);
5001 if(this.badge !== ''){
5002 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5007 onClick : function(e)
5014 if(this.preventDefault){
5018 this.fireEvent('click', this);
5021 disable : function()
5023 this.setDisabled(true);
5028 this.setDisabled(false);
5031 setDisabled : function(state)
5033 if(this.disabled == state){
5037 this.disabled = state;
5040 this.el.addClass('disabled');
5044 this.el.removeClass('disabled');
5049 setActive : function(state)
5051 if(this.active == state){
5055 this.active = state;
5058 this.el.addClass('active');
5062 this.el.removeClass('active');
5067 isActive: function ()
5072 setBadge : function(str)
5078 this.badgeEl.dom.innerHTML = str;
5095 * @class Roo.bootstrap.Row
5096 * @extends Roo.bootstrap.Component
5097 * Bootstrap Row class (contains columns...)
5101 * @param {Object} config The config object
5104 Roo.bootstrap.Row = function(config){
5105 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5108 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5110 getAutoCreate : function(){
5129 * @class Roo.bootstrap.Element
5130 * @extends Roo.bootstrap.Component
5131 * Bootstrap Element class
5132 * @cfg {String} html contents of the element
5133 * @cfg {String} tag tag of the element
5134 * @cfg {String} cls class of the element
5135 * @cfg {Boolean} preventDefault (true|false) default false
5136 * @cfg {Boolean} clickable (true|false) default false
5139 * Create a new Element
5140 * @param {Object} config The config object
5143 Roo.bootstrap.Element = function(config){
5144 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5150 * When a element is chick
5151 * @param {Roo.bootstrap.Element} this
5152 * @param {Roo.EventObject} e
5158 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5163 preventDefault: false,
5166 getAutoCreate : function(){
5170 // cls: this.cls, double assign in parent class Component.js :: onRender
5177 initEvents: function()
5179 Roo.bootstrap.Element.superclass.initEvents.call(this);
5182 this.el.on('click', this.onClick, this);
5187 onClick : function(e)
5189 if(this.preventDefault){
5193 this.fireEvent('click', this, e);
5196 getValue : function()
5198 return this.el.dom.innerHTML;
5201 setValue : function(value)
5203 this.el.dom.innerHTML = value;
5218 * @class Roo.bootstrap.Pagination
5219 * @extends Roo.bootstrap.Component
5220 * Bootstrap Pagination class
5221 * @cfg {String} size xs | sm | md | lg
5222 * @cfg {Boolean} inverse false | true
5225 * Create a new Pagination
5226 * @param {Object} config The config object
5229 Roo.bootstrap.Pagination = function(config){
5230 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5233 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5239 getAutoCreate : function(){
5245 cfg.cls += ' inverse';
5251 cfg.cls += " " + this.cls;
5269 * @class Roo.bootstrap.PaginationItem
5270 * @extends Roo.bootstrap.Component
5271 * Bootstrap PaginationItem class
5272 * @cfg {String} html text
5273 * @cfg {String} href the link
5274 * @cfg {Boolean} preventDefault (true | false) default true
5275 * @cfg {Boolean} active (true | false) default false
5276 * @cfg {Boolean} disabled default false
5280 * Create a new PaginationItem
5281 * @param {Object} config The config object
5285 Roo.bootstrap.PaginationItem = function(config){
5286 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5291 * The raw click event for the entire grid.
5292 * @param {Roo.EventObject} e
5298 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5302 preventDefault: true,
5307 getAutoCreate : function(){
5313 href : this.href ? this.href : '#',
5314 html : this.html ? this.html : ''
5324 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5328 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5334 initEvents: function() {
5336 this.el.on('click', this.onClick, this);
5339 onClick : function(e)
5341 Roo.log('PaginationItem on click ');
5342 if(this.preventDefault){
5350 this.fireEvent('click', this, e);
5366 * @class Roo.bootstrap.Slider
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Slider class
5371 * Create a new Slider
5372 * @param {Object} config The config object
5375 Roo.bootstrap.Slider = function(config){
5376 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5379 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5381 getAutoCreate : function(){
5385 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5389 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5401 * Ext JS Library 1.1.1
5402 * Copyright(c) 2006-2007, Ext JS, LLC.
5404 * Originally Released Under LGPL - original licence link has changed is not relivant.
5407 * <script type="text/javascript">
5412 * @class Roo.grid.ColumnModel
5413 * @extends Roo.util.Observable
5414 * This is the default implementation of a ColumnModel used by the Grid. It defines
5415 * the columns in the grid.
5418 var colModel = new Roo.grid.ColumnModel([
5419 {header: "Ticker", width: 60, sortable: true, locked: true},
5420 {header: "Company Name", width: 150, sortable: true},
5421 {header: "Market Cap.", width: 100, sortable: true},
5422 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5423 {header: "Employees", width: 100, sortable: true, resizable: false}
5428 * The config options listed for this class are options which may appear in each
5429 * individual column definition.
5430 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5432 * @param {Object} config An Array of column config objects. See this class's
5433 * config objects for details.
5435 Roo.grid.ColumnModel = function(config){
5437 * The config passed into the constructor
5439 this.config = config;
5442 // if no id, create one
5443 // if the column does not have a dataIndex mapping,
5444 // map it to the order it is in the config
5445 for(var i = 0, len = config.length; i < len; i++){
5447 if(typeof c.dataIndex == "undefined"){
5450 if(typeof c.renderer == "string"){
5451 c.renderer = Roo.util.Format[c.renderer];
5453 if(typeof c.id == "undefined"){
5456 if(c.editor && c.editor.xtype){
5457 c.editor = Roo.factory(c.editor, Roo.grid);
5459 if(c.editor && c.editor.isFormField){
5460 c.editor = new Roo.grid.GridEditor(c.editor);
5462 this.lookup[c.id] = c;
5466 * The width of columns which have no width specified (defaults to 100)
5469 this.defaultWidth = 100;
5472 * Default sortable of columns which have no sortable specified (defaults to false)
5475 this.defaultSortable = false;
5479 * @event widthchange
5480 * Fires when the width of a column changes.
5481 * @param {ColumnModel} this
5482 * @param {Number} columnIndex The column index
5483 * @param {Number} newWidth The new width
5485 "widthchange": true,
5487 * @event headerchange
5488 * Fires when the text of a header changes.
5489 * @param {ColumnModel} this
5490 * @param {Number} columnIndex The column index
5491 * @param {Number} newText The new header text
5493 "headerchange": true,
5495 * @event hiddenchange
5496 * Fires when a column is hidden or "unhidden".
5497 * @param {ColumnModel} this
5498 * @param {Number} columnIndex The column index
5499 * @param {Boolean} hidden true if hidden, false otherwise
5501 "hiddenchange": true,
5503 * @event columnmoved
5504 * Fires when a column is moved.
5505 * @param {ColumnModel} this
5506 * @param {Number} oldIndex
5507 * @param {Number} newIndex
5509 "columnmoved" : true,
5511 * @event columlockchange
5512 * Fires when a column's locked state is changed
5513 * @param {ColumnModel} this
5514 * @param {Number} colIndex
5515 * @param {Boolean} locked true if locked
5517 "columnlockchange" : true
5519 Roo.grid.ColumnModel.superclass.constructor.call(this);
5521 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5523 * @cfg {String} header The header text to display in the Grid view.
5526 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5527 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5528 * specified, the column's index is used as an index into the Record's data Array.
5531 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5532 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5535 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5536 * Defaults to the value of the {@link #defaultSortable} property.
5537 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5540 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5543 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5546 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5549 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5552 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5553 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5554 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5555 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5558 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5561 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5564 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5567 * @cfg {String} cursor (Optional)
5570 * @cfg {String} tooltip (Optional)
5573 * @cfg {Number} xs (Optional)
5576 * @cfg {Number} sm (Optional)
5579 * @cfg {Number} md (Optional)
5582 * @cfg {Number} lg (Optional)
5585 * Returns the id of the column at the specified index.
5586 * @param {Number} index The column index
5587 * @return {String} the id
5589 getColumnId : function(index){
5590 return this.config[index].id;
5594 * Returns the column for a specified id.
5595 * @param {String} id The column id
5596 * @return {Object} the column
5598 getColumnById : function(id){
5599 return this.lookup[id];
5604 * Returns the column for a specified dataIndex.
5605 * @param {String} dataIndex The column dataIndex
5606 * @return {Object|Boolean} the column or false if not found
5608 getColumnByDataIndex: function(dataIndex){
5609 var index = this.findColumnIndex(dataIndex);
5610 return index > -1 ? this.config[index] : false;
5614 * Returns the index for a specified column id.
5615 * @param {String} id The column id
5616 * @return {Number} the index, or -1 if not found
5618 getIndexById : function(id){
5619 for(var i = 0, len = this.config.length; i < len; i++){
5620 if(this.config[i].id == id){
5628 * Returns the index for a specified column dataIndex.
5629 * @param {String} dataIndex The column dataIndex
5630 * @return {Number} the index, or -1 if not found
5633 findColumnIndex : function(dataIndex){
5634 for(var i = 0, len = this.config.length; i < len; i++){
5635 if(this.config[i].dataIndex == dataIndex){
5643 moveColumn : function(oldIndex, newIndex){
5644 var c = this.config[oldIndex];
5645 this.config.splice(oldIndex, 1);
5646 this.config.splice(newIndex, 0, c);
5647 this.dataMap = null;
5648 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5651 isLocked : function(colIndex){
5652 return this.config[colIndex].locked === true;
5655 setLocked : function(colIndex, value, suppressEvent){
5656 if(this.isLocked(colIndex) == value){
5659 this.config[colIndex].locked = value;
5661 this.fireEvent("columnlockchange", this, colIndex, value);
5665 getTotalLockedWidth : function(){
5667 for(var i = 0; i < this.config.length; i++){
5668 if(this.isLocked(i) && !this.isHidden(i)){
5669 this.totalWidth += this.getColumnWidth(i);
5675 getLockedCount : function(){
5676 for(var i = 0, len = this.config.length; i < len; i++){
5677 if(!this.isLocked(i)){
5682 return this.config.length;
5686 * Returns the number of columns.
5689 getColumnCount : function(visibleOnly){
5690 if(visibleOnly === true){
5692 for(var i = 0, len = this.config.length; i < len; i++){
5693 if(!this.isHidden(i)){
5699 return this.config.length;
5703 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5704 * @param {Function} fn
5705 * @param {Object} scope (optional)
5706 * @return {Array} result
5708 getColumnsBy : function(fn, scope){
5710 for(var i = 0, len = this.config.length; i < len; i++){
5711 var c = this.config[i];
5712 if(fn.call(scope||this, c, i) === true){
5720 * Returns true if the specified column is sortable.
5721 * @param {Number} col The column index
5724 isSortable : function(col){
5725 if(typeof this.config[col].sortable == "undefined"){
5726 return this.defaultSortable;
5728 return this.config[col].sortable;
5732 * Returns the rendering (formatting) function defined for the column.
5733 * @param {Number} col The column index.
5734 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5736 getRenderer : function(col){
5737 if(!this.config[col].renderer){
5738 return Roo.grid.ColumnModel.defaultRenderer;
5740 return this.config[col].renderer;
5744 * Sets the rendering (formatting) function for a column.
5745 * @param {Number} col The column index
5746 * @param {Function} fn The function to use to process the cell's raw data
5747 * to return HTML markup for the grid view. The render function is called with
5748 * the following parameters:<ul>
5749 * <li>Data value.</li>
5750 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5751 * <li>css A CSS style string to apply to the table cell.</li>
5752 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5753 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5754 * <li>Row index</li>
5755 * <li>Column index</li>
5756 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5758 setRenderer : function(col, fn){
5759 this.config[col].renderer = fn;
5763 * Returns the width for the specified column.
5764 * @param {Number} col The column index
5767 getColumnWidth : function(col){
5768 return this.config[col].width * 1 || this.defaultWidth;
5772 * Sets the width for a column.
5773 * @param {Number} col The column index
5774 * @param {Number} width The new width
5776 setColumnWidth : function(col, width, suppressEvent){
5777 this.config[col].width = width;
5778 this.totalWidth = null;
5780 this.fireEvent("widthchange", this, col, width);
5785 * Returns the total width of all columns.
5786 * @param {Boolean} includeHidden True to include hidden column widths
5789 getTotalWidth : function(includeHidden){
5790 if(!this.totalWidth){
5791 this.totalWidth = 0;
5792 for(var i = 0, len = this.config.length; i < len; i++){
5793 if(includeHidden || !this.isHidden(i)){
5794 this.totalWidth += this.getColumnWidth(i);
5798 return this.totalWidth;
5802 * Returns the header for the specified column.
5803 * @param {Number} col The column index
5806 getColumnHeader : function(col){
5807 return this.config[col].header;
5811 * Sets the header for a column.
5812 * @param {Number} col The column index
5813 * @param {String} header The new header
5815 setColumnHeader : function(col, header){
5816 this.config[col].header = header;
5817 this.fireEvent("headerchange", this, col, header);
5821 * Returns the tooltip for the specified column.
5822 * @param {Number} col The column index
5825 getColumnTooltip : function(col){
5826 return this.config[col].tooltip;
5829 * Sets the tooltip for a column.
5830 * @param {Number} col The column index
5831 * @param {String} tooltip The new tooltip
5833 setColumnTooltip : function(col, tooltip){
5834 this.config[col].tooltip = tooltip;
5838 * Returns the dataIndex for the specified column.
5839 * @param {Number} col The column index
5842 getDataIndex : function(col){
5843 return this.config[col].dataIndex;
5847 * Sets the dataIndex for a column.
5848 * @param {Number} col The column index
5849 * @param {Number} dataIndex The new dataIndex
5851 setDataIndex : function(col, dataIndex){
5852 this.config[col].dataIndex = dataIndex;
5858 * Returns true if the cell is editable.
5859 * @param {Number} colIndex The column index
5860 * @param {Number} rowIndex The row index - this is nto actually used..?
5863 isCellEditable : function(colIndex, rowIndex){
5864 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5868 * Returns the editor defined for the cell/column.
5869 * return false or null to disable editing.
5870 * @param {Number} colIndex The column index
5871 * @param {Number} rowIndex The row index
5874 getCellEditor : function(colIndex, rowIndex){
5875 return this.config[colIndex].editor;
5879 * Sets if a column is editable.
5880 * @param {Number} col The column index
5881 * @param {Boolean} editable True if the column is editable
5883 setEditable : function(col, editable){
5884 this.config[col].editable = editable;
5889 * Returns true if the column is hidden.
5890 * @param {Number} colIndex The column index
5893 isHidden : function(colIndex){
5894 return this.config[colIndex].hidden;
5899 * Returns true if the column width cannot be changed
5901 isFixed : function(colIndex){
5902 return this.config[colIndex].fixed;
5906 * Returns true if the column can be resized
5909 isResizable : function(colIndex){
5910 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5913 * Sets if a column is hidden.
5914 * @param {Number} colIndex The column index
5915 * @param {Boolean} hidden True if the column is hidden
5917 setHidden : function(colIndex, hidden){
5918 this.config[colIndex].hidden = hidden;
5919 this.totalWidth = null;
5920 this.fireEvent("hiddenchange", this, colIndex, hidden);
5924 * Sets the editor for a column.
5925 * @param {Number} col The column index
5926 * @param {Object} editor The editor object
5928 setEditor : function(col, editor){
5929 this.config[col].editor = editor;
5933 Roo.grid.ColumnModel.defaultRenderer = function(value)
5935 if(typeof value == "object") {
5938 if(typeof value == "string" && value.length < 1){
5942 return String.format("{0}", value);
5945 // Alias for backwards compatibility
5946 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5949 * Ext JS Library 1.1.1
5950 * Copyright(c) 2006-2007, Ext JS, LLC.
5952 * Originally Released Under LGPL - original licence link has changed is not relivant.
5955 * <script type="text/javascript">
5959 * @class Roo.LoadMask
5960 * A simple utility class for generically masking elements while loading data. If the element being masked has
5961 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5962 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5963 * element's UpdateManager load indicator and will be destroyed after the initial load.
5965 * Create a new LoadMask
5966 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5967 * @param {Object} config The config object
5969 Roo.LoadMask = function(el, config){
5970 this.el = Roo.get(el);
5971 Roo.apply(this, config);
5973 this.store.on('beforeload', this.onBeforeLoad, this);
5974 this.store.on('load', this.onLoad, this);
5975 this.store.on('loadexception', this.onLoadException, this);
5976 this.removeMask = false;
5978 var um = this.el.getUpdateManager();
5979 um.showLoadIndicator = false; // disable the default indicator
5980 um.on('beforeupdate', this.onBeforeLoad, this);
5981 um.on('update', this.onLoad, this);
5982 um.on('failure', this.onLoad, this);
5983 this.removeMask = true;
5987 Roo.LoadMask.prototype = {
5989 * @cfg {Boolean} removeMask
5990 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5991 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5995 * The text to display in a centered loading message box (defaults to 'Loading...')
5999 * @cfg {String} msgCls
6000 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6002 msgCls : 'x-mask-loading',
6005 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6011 * Disables the mask to prevent it from being displayed
6013 disable : function(){
6014 this.disabled = true;
6018 * Enables the mask so that it can be displayed
6020 enable : function(){
6021 this.disabled = false;
6024 onLoadException : function()
6028 if (typeof(arguments[3]) != 'undefined') {
6029 Roo.MessageBox.alert("Error loading",arguments[3]);
6033 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6034 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6041 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6046 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6050 onBeforeLoad : function(){
6052 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6057 destroy : function(){
6059 this.store.un('beforeload', this.onBeforeLoad, this);
6060 this.store.un('load', this.onLoad, this);
6061 this.store.un('loadexception', this.onLoadException, this);
6063 var um = this.el.getUpdateManager();
6064 um.un('beforeupdate', this.onBeforeLoad, this);
6065 um.un('update', this.onLoad, this);
6066 um.un('failure', this.onLoad, this);
6077 * @class Roo.bootstrap.Table
6078 * @extends Roo.bootstrap.Component
6079 * Bootstrap Table class
6080 * @cfg {String} cls table class
6081 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6082 * @cfg {String} bgcolor Specifies the background color for a table
6083 * @cfg {Number} border Specifies whether the table cells should have borders or not
6084 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6085 * @cfg {Number} cellspacing Specifies the space between cells
6086 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6087 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6088 * @cfg {String} sortable Specifies that the table should be sortable
6089 * @cfg {String} summary Specifies a summary of the content of a table
6090 * @cfg {Number} width Specifies the width of a table
6091 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6093 * @cfg {boolean} striped Should the rows be alternative striped
6094 * @cfg {boolean} bordered Add borders to the table
6095 * @cfg {boolean} hover Add hover highlighting
6096 * @cfg {boolean} condensed Format condensed
6097 * @cfg {boolean} responsive Format condensed
6098 * @cfg {Boolean} loadMask (true|false) default false
6099 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6100 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6101 * @cfg {Boolean} rowSelection (true|false) default false
6102 * @cfg {Boolean} cellSelection (true|false) default false
6103 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6104 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6105 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6106 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6110 * Create a new Table
6111 * @param {Object} config The config object
6114 Roo.bootstrap.Table = function(config){
6115 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6120 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6121 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6122 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6123 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6125 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6127 this.sm.grid = this;
6128 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6129 this.sm = this.selModel;
6130 this.sm.xmodule = this.xmodule || false;
6133 if (this.cm && typeof(this.cm.config) == 'undefined') {
6134 this.colModel = new Roo.grid.ColumnModel(this.cm);
6135 this.cm = this.colModel;
6136 this.cm.xmodule = this.xmodule || false;
6139 this.store= Roo.factory(this.store, Roo.data);
6140 this.ds = this.store;
6141 this.ds.xmodule = this.xmodule || false;
6144 if (this.footer && this.store) {
6145 this.footer.dataSource = this.ds;
6146 this.footer = Roo.factory(this.footer);
6153 * Fires when a cell is clicked
6154 * @param {Roo.bootstrap.Table} this
6155 * @param {Roo.Element} el
6156 * @param {Number} rowIndex
6157 * @param {Number} columnIndex
6158 * @param {Roo.EventObject} e
6162 * @event celldblclick
6163 * Fires when a cell is double clicked
6164 * @param {Roo.bootstrap.Table} this
6165 * @param {Roo.Element} el
6166 * @param {Number} rowIndex
6167 * @param {Number} columnIndex
6168 * @param {Roo.EventObject} e
6170 "celldblclick" : true,
6173 * Fires when a row is clicked
6174 * @param {Roo.bootstrap.Table} this
6175 * @param {Roo.Element} el
6176 * @param {Number} rowIndex
6177 * @param {Roo.EventObject} e
6181 * @event rowdblclick
6182 * Fires when a row is double clicked
6183 * @param {Roo.bootstrap.Table} this
6184 * @param {Roo.Element} el
6185 * @param {Number} rowIndex
6186 * @param {Roo.EventObject} e
6188 "rowdblclick" : true,
6191 * Fires when a mouseover occur
6192 * @param {Roo.bootstrap.Table} this
6193 * @param {Roo.Element} el
6194 * @param {Number} rowIndex
6195 * @param {Number} columnIndex
6196 * @param {Roo.EventObject} e
6201 * Fires when a mouseout occur
6202 * @param {Roo.bootstrap.Table} this
6203 * @param {Roo.Element} el
6204 * @param {Number} rowIndex
6205 * @param {Number} columnIndex
6206 * @param {Roo.EventObject} e
6211 * Fires when a row is rendered, so you can change add a style to it.
6212 * @param {Roo.bootstrap.Table} this
6213 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6217 * @event rowsrendered
6218 * Fires when all the rows have been rendered
6219 * @param {Roo.bootstrap.Table} this
6221 'rowsrendered' : true,
6223 * @event contextmenu
6224 * The raw contextmenu event for the entire grid.
6225 * @param {Roo.EventObject} e
6227 "contextmenu" : true,
6229 * @event rowcontextmenu
6230 * Fires when a row is right clicked
6231 * @param {Roo.bootstrap.Table} this
6232 * @param {Number} rowIndex
6233 * @param {Roo.EventObject} e
6235 "rowcontextmenu" : true,
6237 * @event cellcontextmenu
6238 * Fires when a cell is right clicked
6239 * @param {Roo.bootstrap.Table} this
6240 * @param {Number} rowIndex
6241 * @param {Number} cellIndex
6242 * @param {Roo.EventObject} e
6244 "cellcontextmenu" : true,
6246 * @event headercontextmenu
6247 * Fires when a header is right clicked
6248 * @param {Roo.bootstrap.Table} this
6249 * @param {Number} columnIndex
6250 * @param {Roo.EventObject} e
6252 "headercontextmenu" : true
6256 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6282 rowSelection : false,
6283 cellSelection : false,
6286 // Roo.Element - the tbody
6288 // Roo.Element - thead element
6291 container: false, // used by gridpanel...
6297 auto_hide_footer : false,
6299 getAutoCreate : function()
6301 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6308 if (this.scrollBody) {
6309 cfg.cls += ' table-body-fixed';
6312 cfg.cls += ' table-striped';
6316 cfg.cls += ' table-hover';
6318 if (this.bordered) {
6319 cfg.cls += ' table-bordered';
6321 if (this.condensed) {
6322 cfg.cls += ' table-condensed';
6324 if (this.responsive) {
6325 cfg.cls += ' table-responsive';
6329 cfg.cls+= ' ' +this.cls;
6332 // this lot should be simplifed...
6345 ].forEach(function(k) {
6353 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6356 if(this.store || this.cm){
6357 if(this.headerShow){
6358 cfg.cn.push(this.renderHeader());
6361 cfg.cn.push(this.renderBody());
6363 if(this.footerShow){
6364 cfg.cn.push(this.renderFooter());
6366 // where does this come from?
6367 //cfg.cls+= ' TableGrid';
6370 return { cn : [ cfg ] };
6373 initEvents : function()
6375 if(!this.store || !this.cm){
6378 if (this.selModel) {
6379 this.selModel.initEvents();
6383 //Roo.log('initEvents with ds!!!!');
6385 this.mainBody = this.el.select('tbody', true).first();
6386 this.mainHead = this.el.select('thead', true).first();
6387 this.mainFoot = this.el.select('tfoot', true).first();
6393 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6394 e.on('click', _this.sort, _this);
6397 this.mainBody.on("click", this.onClick, this);
6398 this.mainBody.on("dblclick", this.onDblClick, this);
6400 // why is this done????? = it breaks dialogs??
6401 //this.parent().el.setStyle('position', 'relative');
6405 this.footer.parentId = this.id;
6406 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6409 this.el.select('tfoot tr td').first().addClass('hide');
6414 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6417 this.store.on('load', this.onLoad, this);
6418 this.store.on('beforeload', this.onBeforeLoad, this);
6419 this.store.on('update', this.onUpdate, this);
6420 this.store.on('add', this.onAdd, this);
6421 this.store.on("clear", this.clear, this);
6423 this.el.on("contextmenu", this.onContextMenu, this);
6425 this.mainBody.on('scroll', this.onBodyScroll, this);
6427 this.cm.on("headerchange", this.onHeaderChange, this);
6429 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6433 onContextMenu : function(e, t)
6435 this.processEvent("contextmenu", e);
6438 processEvent : function(name, e)
6440 if (name != 'touchstart' ) {
6441 this.fireEvent(name, e);
6444 var t = e.getTarget();
6446 var cell = Roo.get(t);
6452 if(cell.findParent('tfoot', false, true)){
6456 if(cell.findParent('thead', false, true)){
6458 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6459 cell = Roo.get(t).findParent('th', false, true);
6461 Roo.log("failed to find th in thead?");
6462 Roo.log(e.getTarget());
6467 var cellIndex = cell.dom.cellIndex;
6469 var ename = name == 'touchstart' ? 'click' : name;
6470 this.fireEvent("header" + ename, this, cellIndex, e);
6475 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6476 cell = Roo.get(t).findParent('td', false, true);
6478 Roo.log("failed to find th in tbody?");
6479 Roo.log(e.getTarget());
6484 var row = cell.findParent('tr', false, true);
6485 var cellIndex = cell.dom.cellIndex;
6486 var rowIndex = row.dom.rowIndex - 1;
6490 this.fireEvent("row" + name, this, rowIndex, e);
6494 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6500 onMouseover : function(e, el)
6502 var cell = Roo.get(el);
6508 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6509 cell = cell.findParent('td', false, true);
6512 var row = cell.findParent('tr', false, true);
6513 var cellIndex = cell.dom.cellIndex;
6514 var rowIndex = row.dom.rowIndex - 1; // start from 0
6516 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6520 onMouseout : function(e, el)
6522 var cell = Roo.get(el);
6528 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6529 cell = cell.findParent('td', false, true);
6532 var row = cell.findParent('tr', false, true);
6533 var cellIndex = cell.dom.cellIndex;
6534 var rowIndex = row.dom.rowIndex - 1; // start from 0
6536 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6540 onClick : function(e, el)
6542 var cell = Roo.get(el);
6544 if(!cell || (!this.cellSelection && !this.rowSelection)){
6548 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549 cell = cell.findParent('td', false, true);
6552 if(!cell || typeof(cell) == 'undefined'){
6556 var row = cell.findParent('tr', false, true);
6558 if(!row || typeof(row) == 'undefined'){
6562 var cellIndex = cell.dom.cellIndex;
6563 var rowIndex = this.getRowIndex(row);
6565 // why??? - should these not be based on SelectionModel?
6566 if(this.cellSelection){
6567 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6570 if(this.rowSelection){
6571 this.fireEvent('rowclick', this, row, rowIndex, e);
6577 onDblClick : function(e,el)
6579 var cell = Roo.get(el);
6581 if(!cell || (!this.cellSelection && !this.rowSelection)){
6585 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586 cell = cell.findParent('td', false, true);
6589 if(!cell || typeof(cell) == 'undefined'){
6593 var row = cell.findParent('tr', false, true);
6595 if(!row || typeof(row) == 'undefined'){
6599 var cellIndex = cell.dom.cellIndex;
6600 var rowIndex = this.getRowIndex(row);
6602 if(this.cellSelection){
6603 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6606 if(this.rowSelection){
6607 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6611 sort : function(e,el)
6613 var col = Roo.get(el);
6615 if(!col.hasClass('sortable')){
6619 var sort = col.attr('sort');
6622 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6626 this.store.sortInfo = {field : sort, direction : dir};
6629 Roo.log("calling footer first");
6630 this.footer.onClick('first');
6633 this.store.load({ params : { start : 0 } });
6637 renderHeader : function()
6645 this.totalWidth = 0;
6647 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6649 var config = cm.config[i];
6653 cls : 'x-hcol-' + i,
6655 html: cm.getColumnHeader(i)
6660 if(typeof(config.sortable) != 'undefined' && config.sortable){
6662 c.html = '<i class="glyphicon"></i>' + c.html;
6665 if(typeof(config.lgHeader) != 'undefined'){
6666 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6669 if(typeof(config.mdHeader) != 'undefined'){
6670 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6673 if(typeof(config.smHeader) != 'undefined'){
6674 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6677 if(typeof(config.xsHeader) != 'undefined'){
6678 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6685 if(typeof(config.tooltip) != 'undefined'){
6686 c.tooltip = config.tooltip;
6689 if(typeof(config.colspan) != 'undefined'){
6690 c.colspan = config.colspan;
6693 if(typeof(config.hidden) != 'undefined' && config.hidden){
6694 c.style += ' display:none;';
6697 if(typeof(config.dataIndex) != 'undefined'){
6698 c.sort = config.dataIndex;
6703 if(typeof(config.align) != 'undefined' && config.align.length){
6704 c.style += ' text-align:' + config.align + ';';
6707 if(typeof(config.width) != 'undefined'){
6708 c.style += ' width:' + config.width + 'px;';
6709 this.totalWidth += config.width;
6711 this.totalWidth += 100; // assume minimum of 100 per column?
6714 if(typeof(config.cls) != 'undefined'){
6715 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6718 ['xs','sm','md','lg'].map(function(size){
6720 if(typeof(config[size]) == 'undefined'){
6724 if (!config[size]) { // 0 = hidden
6725 c.cls += ' hidden-' + size;
6729 c.cls += ' col-' + size + '-' + config[size];
6739 renderBody : function()
6749 colspan : this.cm.getColumnCount()
6759 renderFooter : function()
6769 colspan : this.cm.getColumnCount()
6783 // Roo.log('ds onload');
6788 var ds = this.store;
6790 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6791 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6792 if (_this.store.sortInfo) {
6794 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6795 e.select('i', true).addClass(['glyphicon-arrow-up']);
6798 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6799 e.select('i', true).addClass(['glyphicon-arrow-down']);
6804 var tbody = this.mainBody;
6806 if(ds.getCount() > 0){
6807 ds.data.each(function(d,rowIndex){
6808 var row = this.renderRow(cm, ds, rowIndex);
6810 tbody.createChild(row);
6814 if(row.cellObjects.length){
6815 Roo.each(row.cellObjects, function(r){
6816 _this.renderCellObject(r);
6823 var tfoot = this.el.select('tfoot', true).first();
6825 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6827 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6829 var total = this.ds.getTotalCount();
6831 if(this.footer.pageSize < total){
6832 this.mainFoot.show();
6836 Roo.each(this.el.select('tbody td', true).elements, function(e){
6837 e.on('mouseover', _this.onMouseover, _this);
6840 Roo.each(this.el.select('tbody td', true).elements, function(e){
6841 e.on('mouseout', _this.onMouseout, _this);
6843 this.fireEvent('rowsrendered', this);
6849 onUpdate : function(ds,record)
6851 this.refreshRow(record);
6855 onRemove : function(ds, record, index, isUpdate){
6856 if(isUpdate !== true){
6857 this.fireEvent("beforerowremoved", this, index, record);
6859 var bt = this.mainBody.dom;
6861 var rows = this.el.select('tbody > tr', true).elements;
6863 if(typeof(rows[index]) != 'undefined'){
6864 bt.removeChild(rows[index].dom);
6867 // if(bt.rows[index]){
6868 // bt.removeChild(bt.rows[index]);
6871 if(isUpdate !== true){
6872 //this.stripeRows(index);
6873 //this.syncRowHeights(index, index);
6875 this.fireEvent("rowremoved", this, index, record);
6879 onAdd : function(ds, records, rowIndex)
6881 //Roo.log('on Add called');
6882 // - note this does not handle multiple adding very well..
6883 var bt = this.mainBody.dom;
6884 for (var i =0 ; i < records.length;i++) {
6885 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6886 //Roo.log(records[i]);
6887 //Roo.log(this.store.getAt(rowIndex+i));
6888 this.insertRow(this.store, rowIndex + i, false);
6895 refreshRow : function(record){
6896 var ds = this.store, index;
6897 if(typeof record == 'number'){
6899 record = ds.getAt(index);
6901 index = ds.indexOf(record);
6903 this.insertRow(ds, index, true);
6905 this.onRemove(ds, record, index+1, true);
6907 //this.syncRowHeights(index, index);
6909 this.fireEvent("rowupdated", this, index, record);
6912 insertRow : function(dm, rowIndex, isUpdate){
6915 this.fireEvent("beforerowsinserted", this, rowIndex);
6917 //var s = this.getScrollState();
6918 var row = this.renderRow(this.cm, this.store, rowIndex);
6919 // insert before rowIndex..
6920 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6924 if(row.cellObjects.length){
6925 Roo.each(row.cellObjects, function(r){
6926 _this.renderCellObject(r);
6931 this.fireEvent("rowsinserted", this, rowIndex);
6932 //this.syncRowHeights(firstRow, lastRow);
6933 //this.stripeRows(firstRow);
6940 getRowDom : function(rowIndex)
6942 var rows = this.el.select('tbody > tr', true).elements;
6944 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6947 // returns the object tree for a tr..
6950 renderRow : function(cm, ds, rowIndex)
6952 var d = ds.getAt(rowIndex);
6956 cls : 'x-row-' + rowIndex,
6960 var cellObjects = [];
6962 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6963 var config = cm.config[i];
6965 var renderer = cm.getRenderer(i);
6969 if(typeof(renderer) !== 'undefined'){
6970 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6972 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6973 // and are rendered into the cells after the row is rendered - using the id for the element.
6975 if(typeof(value) === 'object'){
6985 rowIndex : rowIndex,
6990 this.fireEvent('rowclass', this, rowcfg);
6994 cls : rowcfg.rowClass + ' x-col-' + i,
6996 html: (typeof(value) === 'object') ? '' : value
7003 if(typeof(config.colspan) != 'undefined'){
7004 td.colspan = config.colspan;
7007 if(typeof(config.hidden) != 'undefined' && config.hidden){
7008 td.style += ' display:none;';
7011 if(typeof(config.align) != 'undefined' && config.align.length){
7012 td.style += ' text-align:' + config.align + ';';
7014 if(typeof(config.valign) != 'undefined' && config.valign.length){
7015 td.style += ' vertical-align:' + config.valign + ';';
7018 if(typeof(config.width) != 'undefined'){
7019 td.style += ' width:' + config.width + 'px;';
7022 if(typeof(config.cursor) != 'undefined'){
7023 td.style += ' cursor:' + config.cursor + ';';
7026 if(typeof(config.cls) != 'undefined'){
7027 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7030 ['xs','sm','md','lg'].map(function(size){
7032 if(typeof(config[size]) == 'undefined'){
7036 if (!config[size]) { // 0 = hidden
7037 td.cls += ' hidden-' + size;
7041 td.cls += ' col-' + size + '-' + config[size];
7049 row.cellObjects = cellObjects;
7057 onBeforeLoad : function()
7066 this.el.select('tbody', true).first().dom.innerHTML = '';
7069 * Show or hide a row.
7070 * @param {Number} rowIndex to show or hide
7071 * @param {Boolean} state hide
7073 setRowVisibility : function(rowIndex, state)
7075 var bt = this.mainBody.dom;
7077 var rows = this.el.select('tbody > tr', true).elements;
7079 if(typeof(rows[rowIndex]) == 'undefined'){
7082 rows[rowIndex].dom.style.display = state ? '' : 'none';
7086 getSelectionModel : function(){
7088 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7090 return this.selModel;
7093 * Render the Roo.bootstrap object from renderder
7095 renderCellObject : function(r)
7099 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7101 var t = r.cfg.render(r.container);
7104 Roo.each(r.cfg.cn, function(c){
7106 container: t.getChildContainer(),
7109 _this.renderCellObject(child);
7114 getRowIndex : function(row)
7118 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7129 * Returns the grid's underlying element = used by panel.Grid
7130 * @return {Element} The element
7132 getGridEl : function(){
7136 * Forces a resize - used by panel.Grid
7137 * @return {Element} The element
7139 autoSize : function()
7141 //var ctr = Roo.get(this.container.dom.parentElement);
7142 var ctr = Roo.get(this.el.dom);
7144 var thd = this.getGridEl().select('thead',true).first();
7145 var tbd = this.getGridEl().select('tbody', true).first();
7146 var tfd = this.getGridEl().select('tfoot', true).first();
7148 var cw = ctr.getWidth();
7152 tbd.setSize(ctr.getWidth(),
7153 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7155 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7158 cw = Math.max(cw, this.totalWidth);
7159 this.getGridEl().select('tr',true).setWidth(cw);
7160 // resize 'expandable coloumn?
7162 return; // we doe not have a view in this design..
7165 onBodyScroll: function()
7167 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7169 this.mainHead.setStyle({
7170 'position' : 'relative',
7171 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7177 var scrollHeight = this.mainBody.dom.scrollHeight;
7179 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7181 var height = this.mainBody.getHeight();
7183 if(scrollHeight - height == scrollTop) {
7185 var total = this.ds.getTotalCount();
7187 if(this.footer.cursor + this.footer.pageSize < total){
7189 this.footer.ds.load({
7191 start : this.footer.cursor + this.footer.pageSize,
7192 limit : this.footer.pageSize
7202 onHeaderChange : function()
7204 var header = this.renderHeader();
7205 var table = this.el.select('table', true).first();
7207 this.mainHead.remove();
7208 this.mainHead = table.createChild(header, this.mainBody, false);
7211 onHiddenChange : function(colModel, colIndex, hidden)
7213 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7214 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7216 this.CSS.updateRule(thSelector, "display", "");
7217 this.CSS.updateRule(tdSelector, "display", "");
7220 this.CSS.updateRule(thSelector, "display", "none");
7221 this.CSS.updateRule(tdSelector, "display", "none");
7224 this.onHeaderChange();
7228 setColumnWidth: function(col_index, width)
7230 // width = "md-2 xs-2..."
7231 if(!this.colModel.config[col_index]) {
7235 var w = width.split(" ");
7237 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7239 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7242 for(var j = 0; j < w.length; j++) {
7248 var size_cls = w[j].split("-");
7250 if(!Number.isInteger(size_cls[1] * 1)) {
7254 if(!this.colModel.config[col_index][size_cls[0]]) {
7258 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7262 h_row[0].classList.replace(
7263 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7264 "col-"+size_cls[0]+"-"+size_cls[1]
7267 for(var i = 0; i < rows.length; i++) {
7269 var size_cls = w[j].split("-");
7271 if(!Number.isInteger(size_cls[1] * 1)) {
7275 if(!this.colModel.config[col_index][size_cls[0]]) {
7279 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7283 rows[i].classList.replace(
7284 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7285 "col-"+size_cls[0]+"-"+size_cls[1]
7289 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7304 * @class Roo.bootstrap.TableCell
7305 * @extends Roo.bootstrap.Component
7306 * Bootstrap TableCell class
7307 * @cfg {String} html cell contain text
7308 * @cfg {String} cls cell class
7309 * @cfg {String} tag cell tag (td|th) default td
7310 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7311 * @cfg {String} align Aligns the content in a cell
7312 * @cfg {String} axis Categorizes cells
7313 * @cfg {String} bgcolor Specifies the background color of a cell
7314 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7315 * @cfg {Number} colspan Specifies the number of columns a cell should span
7316 * @cfg {String} headers Specifies one or more header cells a cell is related to
7317 * @cfg {Number} height Sets the height of a cell
7318 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7319 * @cfg {Number} rowspan Sets the number of rows a cell should span
7320 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7321 * @cfg {String} valign Vertical aligns the content in a cell
7322 * @cfg {Number} width Specifies the width of a cell
7325 * Create a new TableCell
7326 * @param {Object} config The config object
7329 Roo.bootstrap.TableCell = function(config){
7330 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7333 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7353 getAutoCreate : function(){
7354 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7374 cfg.align=this.align
7380 cfg.bgcolor=this.bgcolor
7383 cfg.charoff=this.charoff
7386 cfg.colspan=this.colspan
7389 cfg.headers=this.headers
7392 cfg.height=this.height
7395 cfg.nowrap=this.nowrap
7398 cfg.rowspan=this.rowspan
7401 cfg.scope=this.scope
7404 cfg.valign=this.valign
7407 cfg.width=this.width
7426 * @class Roo.bootstrap.TableRow
7427 * @extends Roo.bootstrap.Component
7428 * Bootstrap TableRow class
7429 * @cfg {String} cls row class
7430 * @cfg {String} align Aligns the content in a table row
7431 * @cfg {String} bgcolor Specifies a background color for a table row
7432 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7433 * @cfg {String} valign Vertical aligns the content in a table row
7436 * Create a new TableRow
7437 * @param {Object} config The config object
7440 Roo.bootstrap.TableRow = function(config){
7441 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7444 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7452 getAutoCreate : function(){
7453 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7463 cfg.align = this.align;
7466 cfg.bgcolor = this.bgcolor;
7469 cfg.charoff = this.charoff;
7472 cfg.valign = this.valign;
7490 * @class Roo.bootstrap.TableBody
7491 * @extends Roo.bootstrap.Component
7492 * Bootstrap TableBody class
7493 * @cfg {String} cls element class
7494 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7495 * @cfg {String} align Aligns the content inside the element
7496 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7497 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7500 * Create a new TableBody
7501 * @param {Object} config The config object
7504 Roo.bootstrap.TableBody = function(config){
7505 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7508 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7516 getAutoCreate : function(){
7517 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7531 cfg.align = this.align;
7534 cfg.charoff = this.charoff;
7537 cfg.valign = this.valign;
7544 // initEvents : function()
7551 // this.store = Roo.factory(this.store, Roo.data);
7552 // this.store.on('load', this.onLoad, this);
7554 // this.store.load();
7558 // onLoad: function ()
7560 // this.fireEvent('load', this);
7570 * Ext JS Library 1.1.1
7571 * Copyright(c) 2006-2007, Ext JS, LLC.
7573 * Originally Released Under LGPL - original licence link has changed is not relivant.
7576 * <script type="text/javascript">
7579 // as we use this in bootstrap.
7580 Roo.namespace('Roo.form');
7582 * @class Roo.form.Action
7583 * Internal Class used to handle form actions
7585 * @param {Roo.form.BasicForm} el The form element or its id
7586 * @param {Object} config Configuration options
7591 // define the action interface
7592 Roo.form.Action = function(form, options){
7594 this.options = options || {};
7597 * Client Validation Failed
7600 Roo.form.Action.CLIENT_INVALID = 'client';
7602 * Server Validation Failed
7605 Roo.form.Action.SERVER_INVALID = 'server';
7607 * Connect to Server Failed
7610 Roo.form.Action.CONNECT_FAILURE = 'connect';
7612 * Reading Data from Server Failed
7615 Roo.form.Action.LOAD_FAILURE = 'load';
7617 Roo.form.Action.prototype = {
7619 failureType : undefined,
7620 response : undefined,
7624 run : function(options){
7629 success : function(response){
7634 handleResponse : function(response){
7638 // default connection failure
7639 failure : function(response){
7641 this.response = response;
7642 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7643 this.form.afterAction(this, false);
7646 processResponse : function(response){
7647 this.response = response;
7648 if(!response.responseText){
7651 this.result = this.handleResponse(response);
7655 // utility functions used internally
7656 getUrl : function(appendParams){
7657 var url = this.options.url || this.form.url || this.form.el.dom.action;
7659 var p = this.getParams();
7661 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7667 getMethod : function(){
7668 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7671 getParams : function(){
7672 var bp = this.form.baseParams;
7673 var p = this.options.params;
7675 if(typeof p == "object"){
7676 p = Roo.urlEncode(Roo.applyIf(p, bp));
7677 }else if(typeof p == 'string' && bp){
7678 p += '&' + Roo.urlEncode(bp);
7681 p = Roo.urlEncode(bp);
7686 createCallback : function(){
7688 success: this.success,
7689 failure: this.failure,
7691 timeout: (this.form.timeout*1000),
7692 upload: this.form.fileUpload ? this.success : undefined
7697 Roo.form.Action.Submit = function(form, options){
7698 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7701 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7704 haveProgress : false,
7705 uploadComplete : false,
7707 // uploadProgress indicator.
7708 uploadProgress : function()
7710 if (!this.form.progressUrl) {
7714 if (!this.haveProgress) {
7715 Roo.MessageBox.progress("Uploading", "Uploading");
7717 if (this.uploadComplete) {
7718 Roo.MessageBox.hide();
7722 this.haveProgress = true;
7724 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7726 var c = new Roo.data.Connection();
7728 url : this.form.progressUrl,
7733 success : function(req){
7734 //console.log(data);
7738 rdata = Roo.decode(req.responseText)
7740 Roo.log("Invalid data from server..");
7744 if (!rdata || !rdata.success) {
7746 Roo.MessageBox.alert(Roo.encode(rdata));
7749 var data = rdata.data;
7751 if (this.uploadComplete) {
7752 Roo.MessageBox.hide();
7757 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7758 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7761 this.uploadProgress.defer(2000,this);
7764 failure: function(data) {
7765 Roo.log('progress url failed ');
7776 // run get Values on the form, so it syncs any secondary forms.
7777 this.form.getValues();
7779 var o = this.options;
7780 var method = this.getMethod();
7781 var isPost = method == 'POST';
7782 if(o.clientValidation === false || this.form.isValid()){
7784 if (this.form.progressUrl) {
7785 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7786 (new Date() * 1) + '' + Math.random());
7791 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7792 form:this.form.el.dom,
7793 url:this.getUrl(!isPost),
7795 params:isPost ? this.getParams() : null,
7796 isUpload: this.form.fileUpload
7799 this.uploadProgress();
7801 }else if (o.clientValidation !== false){ // client validation failed
7802 this.failureType = Roo.form.Action.CLIENT_INVALID;
7803 this.form.afterAction(this, false);
7807 success : function(response)
7809 this.uploadComplete= true;
7810 if (this.haveProgress) {
7811 Roo.MessageBox.hide();
7815 var result = this.processResponse(response);
7816 if(result === true || result.success){
7817 this.form.afterAction(this, true);
7821 this.form.markInvalid(result.errors);
7822 this.failureType = Roo.form.Action.SERVER_INVALID;
7824 this.form.afterAction(this, false);
7826 failure : function(response)
7828 this.uploadComplete= true;
7829 if (this.haveProgress) {
7830 Roo.MessageBox.hide();
7833 this.response = response;
7834 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7835 this.form.afterAction(this, false);
7838 handleResponse : function(response){
7839 if(this.form.errorReader){
7840 var rs = this.form.errorReader.read(response);
7843 for(var i = 0, len = rs.records.length; i < len; i++) {
7844 var r = rs.records[i];
7848 if(errors.length < 1){
7852 success : rs.success,
7858 ret = Roo.decode(response.responseText);
7862 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7872 Roo.form.Action.Load = function(form, options){
7873 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7874 this.reader = this.form.reader;
7877 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7882 Roo.Ajax.request(Roo.apply(
7883 this.createCallback(), {
7884 method:this.getMethod(),
7885 url:this.getUrl(false),
7886 params:this.getParams()
7890 success : function(response){
7892 var result = this.processResponse(response);
7893 if(result === true || !result.success || !result.data){
7894 this.failureType = Roo.form.Action.LOAD_FAILURE;
7895 this.form.afterAction(this, false);
7898 this.form.clearInvalid();
7899 this.form.setValues(result.data);
7900 this.form.afterAction(this, true);
7903 handleResponse : function(response){
7904 if(this.form.reader){
7905 var rs = this.form.reader.read(response);
7906 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7908 success : rs.success,
7912 return Roo.decode(response.responseText);
7916 Roo.form.Action.ACTION_TYPES = {
7917 'load' : Roo.form.Action.Load,
7918 'submit' : Roo.form.Action.Submit
7927 * @class Roo.bootstrap.Form
7928 * @extends Roo.bootstrap.Component
7929 * Bootstrap Form class
7930 * @cfg {String} method GET | POST (default POST)
7931 * @cfg {String} labelAlign top | left (default top)
7932 * @cfg {String} align left | right - for navbars
7933 * @cfg {Boolean} loadMask load mask when submit (default true)
7938 * @param {Object} config The config object
7942 Roo.bootstrap.Form = function(config){
7944 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7946 Roo.bootstrap.Form.popover.apply();
7950 * @event clientvalidation
7951 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7952 * @param {Form} this
7953 * @param {Boolean} valid true if the form has passed client-side validation
7955 clientvalidation: true,
7957 * @event beforeaction
7958 * Fires before any action is performed. Return false to cancel the action.
7959 * @param {Form} this
7960 * @param {Action} action The action to be performed
7964 * @event actionfailed
7965 * Fires when an action fails.
7966 * @param {Form} this
7967 * @param {Action} action The action that failed
7969 actionfailed : true,
7971 * @event actioncomplete
7972 * Fires when an action is completed.
7973 * @param {Form} this
7974 * @param {Action} action The action that completed
7976 actioncomplete : true
7980 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7983 * @cfg {String} method
7984 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7989 * The URL to use for form actions if one isn't supplied in the action options.
7992 * @cfg {Boolean} fileUpload
7993 * Set to true if this form is a file upload.
7997 * @cfg {Object} baseParams
7998 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8002 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8006 * @cfg {Sting} align (left|right) for navbar forms
8011 activeAction : null,
8014 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8015 * element by passing it or its id or mask the form itself by passing in true.
8018 waitMsgTarget : false,
8023 * @cfg {Boolean} errorMask (true|false) default false
8028 * @cfg {Number} maskOffset Default 100
8033 * @cfg {Boolean} maskBody
8037 getAutoCreate : function(){
8041 method : this.method || 'POST',
8042 id : this.id || Roo.id(),
8045 if (this.parent().xtype.match(/^Nav/)) {
8046 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8050 if (this.labelAlign == 'left' ) {
8051 cfg.cls += ' form-horizontal';
8057 initEvents : function()
8059 this.el.on('submit', this.onSubmit, this);
8060 // this was added as random key presses on the form where triggering form submit.
8061 this.el.on('keypress', function(e) {
8062 if (e.getCharCode() != 13) {
8065 // we might need to allow it for textareas.. and some other items.
8066 // check e.getTarget().
8068 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8072 Roo.log("keypress blocked");
8080 onSubmit : function(e){
8085 * Returns true if client-side validation on the form is successful.
8088 isValid : function(){
8089 var items = this.getItems();
8093 items.each(function(f){
8099 Roo.log('invalid field: ' + f.name);
8103 if(!target && f.el.isVisible(true)){
8109 if(this.errorMask && !valid){
8110 Roo.bootstrap.Form.popover.mask(this, target);
8117 * Returns true if any fields in this form have changed since their original load.
8120 isDirty : function(){
8122 var items = this.getItems();
8123 items.each(function(f){
8133 * Performs a predefined action (submit or load) or custom actions you define on this form.
8134 * @param {String} actionName The name of the action type
8135 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8136 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8137 * accept other config options):
8139 Property Type Description
8140 ---------------- --------------- ----------------------------------------------------------------------------------
8141 url String The url for the action (defaults to the form's url)
8142 method String The form method to use (defaults to the form's method, or POST if not defined)
8143 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8144 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8145 validate the form on the client (defaults to false)
8147 * @return {BasicForm} this
8149 doAction : function(action, options){
8150 if(typeof action == 'string'){
8151 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8153 if(this.fireEvent('beforeaction', this, action) !== false){
8154 this.beforeAction(action);
8155 action.run.defer(100, action);
8161 beforeAction : function(action){
8162 var o = action.options;
8167 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8169 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8172 // not really supported yet.. ??
8174 //if(this.waitMsgTarget === true){
8175 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8176 //}else if(this.waitMsgTarget){
8177 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8178 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8180 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8186 afterAction : function(action, success){
8187 this.activeAction = null;
8188 var o = action.options;
8193 Roo.get(document.body).unmask();
8199 //if(this.waitMsgTarget === true){
8200 // this.el.unmask();
8201 //}else if(this.waitMsgTarget){
8202 // this.waitMsgTarget.unmask();
8204 // Roo.MessageBox.updateProgress(1);
8205 // Roo.MessageBox.hide();
8212 Roo.callback(o.success, o.scope, [this, action]);
8213 this.fireEvent('actioncomplete', this, action);
8217 // failure condition..
8218 // we have a scenario where updates need confirming.
8219 // eg. if a locking scenario exists..
8220 // we look for { errors : { needs_confirm : true }} in the response.
8222 (typeof(action.result) != 'undefined') &&
8223 (typeof(action.result.errors) != 'undefined') &&
8224 (typeof(action.result.errors.needs_confirm) != 'undefined')
8227 Roo.log("not supported yet");
8230 Roo.MessageBox.confirm(
8231 "Change requires confirmation",
8232 action.result.errorMsg,
8237 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8247 Roo.callback(o.failure, o.scope, [this, action]);
8248 // show an error message if no failed handler is set..
8249 if (!this.hasListener('actionfailed')) {
8250 Roo.log("need to add dialog support");
8252 Roo.MessageBox.alert("Error",
8253 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8254 action.result.errorMsg :
8255 "Saving Failed, please check your entries or try again"
8260 this.fireEvent('actionfailed', this, action);
8265 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8266 * @param {String} id The value to search for
8269 findField : function(id){
8270 var items = this.getItems();
8271 var field = items.get(id);
8273 items.each(function(f){
8274 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8281 return field || null;
8284 * Mark fields in this form invalid in bulk.
8285 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8286 * @return {BasicForm} this
8288 markInvalid : function(errors){
8289 if(errors instanceof Array){
8290 for(var i = 0, len = errors.length; i < len; i++){
8291 var fieldError = errors[i];
8292 var f = this.findField(fieldError.id);
8294 f.markInvalid(fieldError.msg);
8300 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8301 field.markInvalid(errors[id]);
8305 //Roo.each(this.childForms || [], function (f) {
8306 // f.markInvalid(errors);
8313 * Set values for fields in this form in bulk.
8314 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8315 * @return {BasicForm} this
8317 setValues : function(values){
8318 if(values instanceof Array){ // array of objects
8319 for(var i = 0, len = values.length; i < len; i++){
8321 var f = this.findField(v.id);
8323 f.setValue(v.value);
8324 if(this.trackResetOnLoad){
8325 f.originalValue = f.getValue();
8329 }else{ // object hash
8332 if(typeof values[id] != 'function' && (field = this.findField(id))){
8334 if (field.setFromData &&
8336 field.displayField &&
8337 // combos' with local stores can
8338 // be queried via setValue()
8339 // to set their value..
8340 (field.store && !field.store.isLocal)
8344 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8345 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8346 field.setFromData(sd);
8348 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8350 field.setFromData(values);
8353 field.setValue(values[id]);
8357 if(this.trackResetOnLoad){
8358 field.originalValue = field.getValue();
8364 //Roo.each(this.childForms || [], function (f) {
8365 // f.setValues(values);
8372 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8373 * they are returned as an array.
8374 * @param {Boolean} asString
8377 getValues : function(asString){
8378 //if (this.childForms) {
8379 // copy values from the child forms
8380 // Roo.each(this.childForms, function (f) {
8381 // this.setValues(f.getValues());
8387 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8388 if(asString === true){
8391 return Roo.urlDecode(fs);
8395 * Returns the fields in this form as an object with key/value pairs.
8396 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8399 getFieldValues : function(with_hidden)
8401 var items = this.getItems();
8403 items.each(function(f){
8409 var v = f.getValue();
8411 if (f.inputType =='radio') {
8412 if (typeof(ret[f.getName()]) == 'undefined') {
8413 ret[f.getName()] = ''; // empty..
8416 if (!f.el.dom.checked) {
8424 if(f.xtype == 'MoneyField'){
8425 ret[f.currencyName] = f.getCurrency();
8428 // not sure if this supported any more..
8429 if ((typeof(v) == 'object') && f.getRawValue) {
8430 v = f.getRawValue() ; // dates..
8432 // combo boxes where name != hiddenName...
8433 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8434 ret[f.name] = f.getRawValue();
8436 ret[f.getName()] = v;
8443 * Clears all invalid messages in this form.
8444 * @return {BasicForm} this
8446 clearInvalid : function(){
8447 var items = this.getItems();
8449 items.each(function(f){
8458 * @return {BasicForm} this
8461 var items = this.getItems();
8462 items.each(function(f){
8466 Roo.each(this.childForms || [], function (f) {
8474 getItems : function()
8476 var r=new Roo.util.MixedCollection(false, function(o){
8477 return o.id || (o.id = Roo.id());
8479 var iter = function(el) {
8486 Roo.each(el.items,function(e) {
8495 hideFields : function(items)
8497 Roo.each(items, function(i){
8499 var f = this.findField(i);
8510 showFields : function(items)
8512 Roo.each(items, function(i){
8514 var f = this.findField(i);
8527 Roo.apply(Roo.bootstrap.Form, {
8554 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8555 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8556 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8557 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8560 this.maskEl.top.enableDisplayMode("block");
8561 this.maskEl.left.enableDisplayMode("block");
8562 this.maskEl.bottom.enableDisplayMode("block");
8563 this.maskEl.right.enableDisplayMode("block");
8565 this.toolTip = new Roo.bootstrap.Tooltip({
8566 cls : 'roo-form-error-popover',
8568 'left' : ['r-l', [-2,0], 'right'],
8569 'right' : ['l-r', [2,0], 'left'],
8570 'bottom' : ['tl-bl', [0,2], 'top'],
8571 'top' : [ 'bl-tl', [0,-2], 'bottom']
8575 this.toolTip.render(Roo.get(document.body));
8577 this.toolTip.el.enableDisplayMode("block");
8579 Roo.get(document.body).on('click', function(){
8583 Roo.get(document.body).on('touchstart', function(){
8587 this.isApplied = true
8590 mask : function(form, target)
8594 this.target = target;
8596 if(!this.form.errorMask || !target.el){
8600 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8602 Roo.log(scrollable);
8604 var ot = this.target.el.calcOffsetsTo(scrollable);
8606 var scrollTo = ot[1] - this.form.maskOffset;
8608 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8610 scrollable.scrollTo('top', scrollTo);
8612 var box = this.target.el.getBox();
8614 var zIndex = Roo.bootstrap.Modal.zIndex++;
8617 this.maskEl.top.setStyle('position', 'absolute');
8618 this.maskEl.top.setStyle('z-index', zIndex);
8619 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8620 this.maskEl.top.setLeft(0);
8621 this.maskEl.top.setTop(0);
8622 this.maskEl.top.show();
8624 this.maskEl.left.setStyle('position', 'absolute');
8625 this.maskEl.left.setStyle('z-index', zIndex);
8626 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8627 this.maskEl.left.setLeft(0);
8628 this.maskEl.left.setTop(box.y - this.padding);
8629 this.maskEl.left.show();
8631 this.maskEl.bottom.setStyle('position', 'absolute');
8632 this.maskEl.bottom.setStyle('z-index', zIndex);
8633 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8634 this.maskEl.bottom.setLeft(0);
8635 this.maskEl.bottom.setTop(box.bottom + this.padding);
8636 this.maskEl.bottom.show();
8638 this.maskEl.right.setStyle('position', 'absolute');
8639 this.maskEl.right.setStyle('z-index', zIndex);
8640 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8641 this.maskEl.right.setLeft(box.right + this.padding);
8642 this.maskEl.right.setTop(box.y - this.padding);
8643 this.maskEl.right.show();
8645 this.toolTip.bindEl = this.target.el;
8647 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8649 var tip = this.target.blankText;
8651 if(this.target.getValue() !== '' ) {
8653 if (this.target.invalidText.length) {
8654 tip = this.target.invalidText;
8655 } else if (this.target.regexText.length){
8656 tip = this.target.regexText;
8660 this.toolTip.show(tip);
8662 this.intervalID = window.setInterval(function() {
8663 Roo.bootstrap.Form.popover.unmask();
8666 window.onwheel = function(){ return false;};
8668 (function(){ this.isMasked = true; }).defer(500, this);
8674 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8678 this.maskEl.top.setStyle('position', 'absolute');
8679 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8680 this.maskEl.top.hide();
8682 this.maskEl.left.setStyle('position', 'absolute');
8683 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8684 this.maskEl.left.hide();
8686 this.maskEl.bottom.setStyle('position', 'absolute');
8687 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8688 this.maskEl.bottom.hide();
8690 this.maskEl.right.setStyle('position', 'absolute');
8691 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8692 this.maskEl.right.hide();
8694 this.toolTip.hide();
8696 this.toolTip.el.hide();
8698 window.onwheel = function(){ return true;};
8700 if(this.intervalID){
8701 window.clearInterval(this.intervalID);
8702 this.intervalID = false;
8705 this.isMasked = false;
8715 * Ext JS Library 1.1.1
8716 * Copyright(c) 2006-2007, Ext JS, LLC.
8718 * Originally Released Under LGPL - original licence link has changed is not relivant.
8721 * <script type="text/javascript">
8724 * @class Roo.form.VTypes
8725 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8728 Roo.form.VTypes = function(){
8729 // closure these in so they are only created once.
8730 var alpha = /^[a-zA-Z_]+$/;
8731 var alphanum = /^[a-zA-Z0-9_]+$/;
8732 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8733 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8735 // All these messages and functions are configurable
8738 * The function used to validate email addresses
8739 * @param {String} value The email address
8741 'email' : function(v){
8742 return email.test(v);
8745 * The error text to display when the email validation function returns false
8748 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8750 * The keystroke filter mask to be applied on email input
8753 'emailMask' : /[a-z0-9_\.\-@]/i,
8756 * The function used to validate URLs
8757 * @param {String} value The URL
8759 'url' : function(v){
8763 * The error text to display when the url validation function returns false
8766 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8769 * The function used to validate alpha values
8770 * @param {String} value The value
8772 'alpha' : function(v){
8773 return alpha.test(v);
8776 * The error text to display when the alpha validation function returns false
8779 'alphaText' : 'This field should only contain letters and _',
8781 * The keystroke filter mask to be applied on alpha input
8784 'alphaMask' : /[a-z_]/i,
8787 * The function used to validate alphanumeric values
8788 * @param {String} value The value
8790 'alphanum' : function(v){
8791 return alphanum.test(v);
8794 * The error text to display when the alphanumeric validation function returns false
8797 'alphanumText' : 'This field should only contain letters, numbers and _',
8799 * The keystroke filter mask to be applied on alphanumeric input
8802 'alphanumMask' : /[a-z0-9_]/i
8812 * @class Roo.bootstrap.Input
8813 * @extends Roo.bootstrap.Component
8814 * Bootstrap Input class
8815 * @cfg {Boolean} disabled is it disabled
8816 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8817 * @cfg {String} name name of the input
8818 * @cfg {string} fieldLabel - the label associated
8819 * @cfg {string} placeholder - placeholder to put in text.
8820 * @cfg {string} before - input group add on before
8821 * @cfg {string} after - input group add on after
8822 * @cfg {string} size - (lg|sm) or leave empty..
8823 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8824 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8825 * @cfg {Number} md colspan out of 12 for computer-sized screens
8826 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8827 * @cfg {string} value default value of the input
8828 * @cfg {Number} labelWidth set the width of label
8829 * @cfg {Number} labellg set the width of label (1-12)
8830 * @cfg {Number} labelmd set the width of label (1-12)
8831 * @cfg {Number} labelsm set the width of label (1-12)
8832 * @cfg {Number} labelxs set the width of label (1-12)
8833 * @cfg {String} labelAlign (top|left)
8834 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8835 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8836 * @cfg {String} indicatorpos (left|right) default left
8837 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8838 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8840 * @cfg {String} align (left|center|right) Default left
8841 * @cfg {Boolean} forceFeedback (true|false) Default false
8844 * Create a new Input
8845 * @param {Object} config The config object
8848 Roo.bootstrap.Input = function(config){
8850 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8855 * Fires when this field receives input focus.
8856 * @param {Roo.form.Field} this
8861 * Fires when this field loses input focus.
8862 * @param {Roo.form.Field} this
8867 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8868 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8869 * @param {Roo.form.Field} this
8870 * @param {Roo.EventObject} e The event object
8875 * Fires just before the field blurs if the field value has changed.
8876 * @param {Roo.form.Field} this
8877 * @param {Mixed} newValue The new value
8878 * @param {Mixed} oldValue The original value
8883 * Fires after the field has been marked as invalid.
8884 * @param {Roo.form.Field} this
8885 * @param {String} msg The validation message
8890 * Fires after the field has been validated with no errors.
8891 * @param {Roo.form.Field} this
8896 * Fires after the key up
8897 * @param {Roo.form.Field} this
8898 * @param {Roo.EventObject} e The event Object
8904 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8906 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8907 automatic validation (defaults to "keyup").
8909 validationEvent : "keyup",
8911 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8913 validateOnBlur : true,
8915 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8917 validationDelay : 250,
8919 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8921 focusClass : "x-form-focus", // not needed???
8925 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8927 invalidClass : "has-warning",
8930 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8932 validClass : "has-success",
8935 * @cfg {Boolean} hasFeedback (true|false) default true
8940 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8942 invalidFeedbackClass : "glyphicon-warning-sign",
8945 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8947 validFeedbackClass : "glyphicon-ok",
8950 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8952 selectOnFocus : false,
8955 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8959 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8964 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8966 disableKeyFilter : false,
8969 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8973 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8977 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8979 blankText : "Please complete this mandatory field",
8982 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8986 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8988 maxLength : Number.MAX_VALUE,
8990 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8992 minLengthText : "The minimum length for this field is {0}",
8994 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8996 maxLengthText : "The maximum length for this field is {0}",
9000 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9001 * If available, this function will be called only after the basic validators all return true, and will be passed the
9002 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9006 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9007 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9008 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9012 * @cfg {String} regexText -- Depricated - use Invalid Text
9017 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9023 autocomplete: false,
9042 formatedValue : false,
9043 forceFeedback : false,
9045 indicatorpos : 'left',
9055 parentLabelAlign : function()
9058 while (parent.parent()) {
9059 parent = parent.parent();
9060 if (typeof(parent.labelAlign) !='undefined') {
9061 return parent.labelAlign;
9068 getAutoCreate : function()
9070 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9076 if(this.inputType != 'hidden'){
9077 cfg.cls = 'form-group' //input-group
9083 type : this.inputType,
9085 cls : 'form-control',
9086 placeholder : this.placeholder || '',
9087 autocomplete : this.autocomplete || 'new-password'
9090 if(this.capture.length){
9091 input.capture = this.capture;
9094 if(this.accept.length){
9095 input.accept = this.accept + "/*";
9099 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9102 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9103 input.maxLength = this.maxLength;
9106 if (this.disabled) {
9107 input.disabled=true;
9110 if (this.readOnly) {
9111 input.readonly=true;
9115 input.name = this.name;
9119 input.cls += ' input-' + this.size;
9123 ['xs','sm','md','lg'].map(function(size){
9124 if (settings[size]) {
9125 cfg.cls += ' col-' + size + '-' + settings[size];
9129 var inputblock = input;
9133 cls: 'glyphicon form-control-feedback'
9136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9139 cls : 'has-feedback',
9147 if (this.before || this.after) {
9150 cls : 'input-group',
9154 if (this.before && typeof(this.before) == 'string') {
9156 inputblock.cn.push({
9158 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9162 if (this.before && typeof(this.before) == 'object') {
9163 this.before = Roo.factory(this.before);
9165 inputblock.cn.push({
9167 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9168 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9172 inputblock.cn.push(input);
9174 if (this.after && typeof(this.after) == 'string') {
9175 inputblock.cn.push({
9177 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9181 if (this.after && typeof(this.after) == 'object') {
9182 this.after = Roo.factory(this.after);
9184 inputblock.cn.push({
9186 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9187 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9191 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9192 inputblock.cls += ' has-feedback';
9193 inputblock.cn.push(feedback);
9198 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9199 tooltip : 'This field is required'
9201 if (Roo.bootstrap.version == 4) {
9204 style : 'display-none'
9207 if (align ==='left' && this.fieldLabel.length) {
9209 cfg.cls += ' roo-form-group-label-left row';
9216 cls : 'control-label col-form-label',
9217 html : this.fieldLabel
9228 var labelCfg = cfg.cn[1];
9229 var contentCfg = cfg.cn[2];
9231 if(this.indicatorpos == 'right'){
9236 cls : 'control-label col-form-label',
9240 html : this.fieldLabel
9254 labelCfg = cfg.cn[0];
9255 contentCfg = cfg.cn[1];
9259 if(this.labelWidth > 12){
9260 labelCfg.style = "width: " + this.labelWidth + 'px';
9263 if(this.labelWidth < 13 && this.labelmd == 0){
9264 this.labelmd = this.labelWidth;
9267 if(this.labellg > 0){
9268 labelCfg.cls += ' col-lg-' + this.labellg;
9269 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9272 if(this.labelmd > 0){
9273 labelCfg.cls += ' col-md-' + this.labelmd;
9274 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9277 if(this.labelsm > 0){
9278 labelCfg.cls += ' col-sm-' + this.labelsm;
9279 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9282 if(this.labelxs > 0){
9283 labelCfg.cls += ' col-xs-' + this.labelxs;
9284 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9288 } else if ( this.fieldLabel.length) {
9293 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9294 tooltip : 'This field is required'
9298 //cls : 'input-group-addon',
9299 html : this.fieldLabel
9307 if(this.indicatorpos == 'right'){
9312 //cls : 'input-group-addon',
9313 html : this.fieldLabel
9318 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9319 tooltip : 'This field is required'
9339 if (this.parentType === 'Navbar' && this.parent().bar) {
9340 cfg.cls += ' navbar-form';
9343 if (this.parentType === 'NavGroup') {
9344 cfg.cls += ' navbar-form';
9352 * return the real input element.
9354 inputEl: function ()
9356 return this.el.select('input.form-control',true).first();
9359 tooltipEl : function()
9361 return this.inputEl();
9364 indicatorEl : function()
9366 if (Roo.bootstrap.version == 4) {
9367 return false; // not enabled in v4 yet.
9370 var indicator = this.el.select('i.roo-required-indicator',true).first();
9380 setDisabled : function(v)
9382 var i = this.inputEl().dom;
9384 i.removeAttribute('disabled');
9388 i.setAttribute('disabled','true');
9390 initEvents : function()
9393 this.inputEl().on("keydown" , this.fireKey, this);
9394 this.inputEl().on("focus", this.onFocus, this);
9395 this.inputEl().on("blur", this.onBlur, this);
9397 this.inputEl().relayEvent('keyup', this);
9399 this.indicator = this.indicatorEl();
9402 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9405 // reference to original value for reset
9406 this.originalValue = this.getValue();
9407 //Roo.form.TextField.superclass.initEvents.call(this);
9408 if(this.validationEvent == 'keyup'){
9409 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9410 this.inputEl().on('keyup', this.filterValidation, this);
9412 else if(this.validationEvent !== false){
9413 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9416 if(this.selectOnFocus){
9417 this.on("focus", this.preFocus, this);
9420 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9421 this.inputEl().on("keypress", this.filterKeys, this);
9423 this.inputEl().relayEvent('keypress', this);
9426 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9427 this.el.on("click", this.autoSize, this);
9430 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9431 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9434 if (typeof(this.before) == 'object') {
9435 this.before.render(this.el.select('.roo-input-before',true).first());
9437 if (typeof(this.after) == 'object') {
9438 this.after.render(this.el.select('.roo-input-after',true).first());
9441 this.inputEl().on('change', this.onChange, this);
9444 filterValidation : function(e){
9445 if(!e.isNavKeyPress()){
9446 this.validationTask.delay(this.validationDelay);
9450 * Validates the field value
9451 * @return {Boolean} True if the value is valid, else false
9453 validate : function(){
9454 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9455 if(this.disabled || this.validateValue(this.getRawValue())){
9466 * Validates a value according to the field's validation rules and marks the field as invalid
9467 * if the validation fails
9468 * @param {Mixed} value The value to validate
9469 * @return {Boolean} True if the value is valid, else false
9471 validateValue : function(value)
9473 if(this.getVisibilityEl().hasClass('hidden')){
9477 if(value.length < 1) { // if it's blank
9478 if(this.allowBlank){
9484 if(value.length < this.minLength){
9487 if(value.length > this.maxLength){
9491 var vt = Roo.form.VTypes;
9492 if(!vt[this.vtype](value, this)){
9496 if(typeof this.validator == "function"){
9497 var msg = this.validator(value);
9501 if (typeof(msg) == 'string') {
9502 this.invalidText = msg;
9506 if(this.regex && !this.regex.test(value)){
9514 fireKey : function(e){
9515 //Roo.log('field ' + e.getKey());
9516 if(e.isNavKeyPress()){
9517 this.fireEvent("specialkey", this, e);
9520 focus : function (selectText){
9522 this.inputEl().focus();
9523 if(selectText === true){
9524 this.inputEl().dom.select();
9530 onFocus : function(){
9531 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9532 // this.el.addClass(this.focusClass);
9535 this.hasFocus = true;
9536 this.startValue = this.getValue();
9537 this.fireEvent("focus", this);
9541 beforeBlur : Roo.emptyFn,
9545 onBlur : function(){
9547 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9548 //this.el.removeClass(this.focusClass);
9550 this.hasFocus = false;
9551 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9554 var v = this.getValue();
9555 if(String(v) !== String(this.startValue)){
9556 this.fireEvent('change', this, v, this.startValue);
9558 this.fireEvent("blur", this);
9561 onChange : function(e)
9563 var v = this.getValue();
9564 if(String(v) !== String(this.startValue)){
9565 this.fireEvent('change', this, v, this.startValue);
9571 * Resets the current field value to the originally loaded value and clears any validation messages
9574 this.setValue(this.originalValue);
9578 * Returns the name of the field
9579 * @return {Mixed} name The name field
9581 getName: function(){
9585 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9586 * @return {Mixed} value The field value
9588 getValue : function(){
9590 var v = this.inputEl().getValue();
9595 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9596 * @return {Mixed} value The field value
9598 getRawValue : function(){
9599 var v = this.inputEl().getValue();
9605 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9606 * @param {Mixed} value The value to set
9608 setRawValue : function(v){
9609 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9612 selectText : function(start, end){
9613 var v = this.getRawValue();
9615 start = start === undefined ? 0 : start;
9616 end = end === undefined ? v.length : end;
9617 var d = this.inputEl().dom;
9618 if(d.setSelectionRange){
9619 d.setSelectionRange(start, end);
9620 }else if(d.createTextRange){
9621 var range = d.createTextRange();
9622 range.moveStart("character", start);
9623 range.moveEnd("character", v.length-end);
9630 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9631 * @param {Mixed} value The value to set
9633 setValue : function(v){
9636 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9642 processValue : function(value){
9643 if(this.stripCharsRe){
9644 var newValue = value.replace(this.stripCharsRe, '');
9645 if(newValue !== value){
9646 this.setRawValue(newValue);
9653 preFocus : function(){
9655 if(this.selectOnFocus){
9656 this.inputEl().dom.select();
9659 filterKeys : function(e){
9661 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9664 var c = e.getCharCode(), cc = String.fromCharCode(c);
9665 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9668 if(!this.maskRe.test(cc)){
9673 * Clear any invalid styles/messages for this field
9675 clearInvalid : function(){
9677 if(!this.el || this.preventMark){ // not rendered
9682 this.el.removeClass(this.invalidClass);
9684 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9686 var feedback = this.el.select('.form-control-feedback', true).first();
9689 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9695 this.indicator.removeClass('visible');
9696 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9699 this.fireEvent('valid', this);
9703 * Mark this field as valid
9705 markValid : function()
9707 if(!this.el || this.preventMark){ // not rendered...
9711 this.el.removeClass([this.invalidClass, this.validClass]);
9713 var feedback = this.el.select('.form-control-feedback', true).first();
9716 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9720 this.indicator.removeClass('visible');
9721 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9728 if(this.allowBlank && !this.getRawValue().length){
9732 this.el.addClass(this.validClass);
9734 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9736 var feedback = this.el.select('.form-control-feedback', true).first();
9739 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9740 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9745 this.fireEvent('valid', this);
9749 * Mark this field as invalid
9750 * @param {String} msg The validation message
9752 markInvalid : function(msg)
9754 if(!this.el || this.preventMark){ // not rendered
9758 this.el.removeClass([this.invalidClass, this.validClass]);
9760 var feedback = this.el.select('.form-control-feedback', true).first();
9763 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9770 if(this.allowBlank && !this.getRawValue().length){
9775 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9776 this.indicator.addClass('visible');
9779 this.el.addClass(this.invalidClass);
9781 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9783 var feedback = this.el.select('.form-control-feedback', true).first();
9786 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9788 if(this.getValue().length || this.forceFeedback){
9789 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9796 this.fireEvent('invalid', this, msg);
9799 SafariOnKeyDown : function(event)
9801 // this is a workaround for a password hang bug on chrome/ webkit.
9802 if (this.inputEl().dom.type != 'password') {
9806 var isSelectAll = false;
9808 if(this.inputEl().dom.selectionEnd > 0){
9809 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9811 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9812 event.preventDefault();
9817 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9819 event.preventDefault();
9820 // this is very hacky as keydown always get's upper case.
9822 var cc = String.fromCharCode(event.getCharCode());
9823 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9827 adjustWidth : function(tag, w){
9828 tag = tag.toLowerCase();
9829 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9830 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9834 if(tag == 'textarea'){
9837 }else if(Roo.isOpera){
9841 if(tag == 'textarea'){
9849 setFieldLabel : function(v)
9855 if(this.indicatorEl()){
9856 var ar = this.el.select('label > span',true);
9858 if (ar.elements.length) {
9859 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9860 this.fieldLabel = v;
9864 var br = this.el.select('label',true);
9866 if(br.elements.length) {
9867 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9868 this.fieldLabel = v;
9872 Roo.log('Cannot Found any of label > span || label in input');
9876 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9877 this.fieldLabel = v;
9892 * @class Roo.bootstrap.TextArea
9893 * @extends Roo.bootstrap.Input
9894 * Bootstrap TextArea class
9895 * @cfg {Number} cols Specifies the visible width of a text area
9896 * @cfg {Number} rows Specifies the visible number of lines in a text area
9897 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9898 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9899 * @cfg {string} html text
9902 * Create a new TextArea
9903 * @param {Object} config The config object
9906 Roo.bootstrap.TextArea = function(config){
9907 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9911 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9921 getAutoCreate : function(){
9923 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9929 if(this.inputType != 'hidden'){
9930 cfg.cls = 'form-group' //input-group
9938 value : this.value || '',
9939 html: this.html || '',
9940 cls : 'form-control',
9941 placeholder : this.placeholder || ''
9945 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9946 input.maxLength = this.maxLength;
9950 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9954 input.cols = this.cols;
9957 if (this.readOnly) {
9958 input.readonly = true;
9962 input.name = this.name;
9966 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9970 ['xs','sm','md','lg'].map(function(size){
9971 if (settings[size]) {
9972 cfg.cls += ' col-' + size + '-' + settings[size];
9976 var inputblock = input;
9978 if(this.hasFeedback && !this.allowBlank){
9982 cls: 'glyphicon form-control-feedback'
9986 cls : 'has-feedback',
9995 if (this.before || this.after) {
9998 cls : 'input-group',
10002 inputblock.cn.push({
10004 cls : 'input-group-addon',
10009 inputblock.cn.push(input);
10011 if(this.hasFeedback && !this.allowBlank){
10012 inputblock.cls += ' has-feedback';
10013 inputblock.cn.push(feedback);
10017 inputblock.cn.push({
10019 cls : 'input-group-addon',
10026 if (align ==='left' && this.fieldLabel.length) {
10031 cls : 'control-label',
10032 html : this.fieldLabel
10043 if(this.labelWidth > 12){
10044 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10047 if(this.labelWidth < 13 && this.labelmd == 0){
10048 this.labelmd = this.labelWidth;
10051 if(this.labellg > 0){
10052 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10053 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10056 if(this.labelmd > 0){
10057 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10058 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10061 if(this.labelsm > 0){
10062 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10063 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10066 if(this.labelxs > 0){
10067 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10068 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10071 } else if ( this.fieldLabel.length) {
10076 //cls : 'input-group-addon',
10077 html : this.fieldLabel
10095 if (this.disabled) {
10096 input.disabled=true;
10103 * return the real textarea element.
10105 inputEl: function ()
10107 return this.el.select('textarea.form-control',true).first();
10111 * Clear any invalid styles/messages for this field
10113 clearInvalid : function()
10116 if(!this.el || this.preventMark){ // not rendered
10120 var label = this.el.select('label', true).first();
10121 var icon = this.el.select('i.fa-star', true).first();
10127 this.el.removeClass(this.invalidClass);
10129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10131 var feedback = this.el.select('.form-control-feedback', true).first();
10134 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10139 this.fireEvent('valid', this);
10143 * Mark this field as valid
10145 markValid : function()
10147 if(!this.el || this.preventMark){ // not rendered
10151 this.el.removeClass([this.invalidClass, this.validClass]);
10153 var feedback = this.el.select('.form-control-feedback', true).first();
10156 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10159 if(this.disabled || this.allowBlank){
10163 var label = this.el.select('label', true).first();
10164 var icon = this.el.select('i.fa-star', true).first();
10170 this.el.addClass(this.validClass);
10172 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10174 var feedback = this.el.select('.form-control-feedback', true).first();
10177 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10178 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10183 this.fireEvent('valid', this);
10187 * Mark this field as invalid
10188 * @param {String} msg The validation message
10190 markInvalid : function(msg)
10192 if(!this.el || this.preventMark){ // not rendered
10196 this.el.removeClass([this.invalidClass, this.validClass]);
10198 var feedback = this.el.select('.form-control-feedback', true).first();
10201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10204 if(this.disabled || this.allowBlank){
10208 var label = this.el.select('label', true).first();
10209 var icon = this.el.select('i.fa-star', true).first();
10211 if(!this.getValue().length && label && !icon){
10212 this.el.createChild({
10214 cls : 'text-danger fa fa-lg fa-star',
10215 tooltip : 'This field is required',
10216 style : 'margin-right:5px;'
10220 this.el.addClass(this.invalidClass);
10222 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10224 var feedback = this.el.select('.form-control-feedback', true).first();
10227 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10229 if(this.getValue().length || this.forceFeedback){
10230 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10237 this.fireEvent('invalid', this, msg);
10245 * trigger field - base class for combo..
10250 * @class Roo.bootstrap.TriggerField
10251 * @extends Roo.bootstrap.Input
10252 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10253 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10254 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10255 * for which you can provide a custom implementation. For example:
10257 var trigger = new Roo.bootstrap.TriggerField();
10258 trigger.onTriggerClick = myTriggerFn;
10259 trigger.applyTo('my-field');
10262 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10263 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10264 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10265 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10266 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10269 * Create a new TriggerField.
10270 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10271 * to the base TextField)
10273 Roo.bootstrap.TriggerField = function(config){
10274 this.mimicing = false;
10275 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10278 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10280 * @cfg {String} triggerClass A CSS class to apply to the trigger
10283 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10288 * @cfg {Boolean} removable (true|false) special filter default false
10292 /** @cfg {Boolean} grow @hide */
10293 /** @cfg {Number} growMin @hide */
10294 /** @cfg {Number} growMax @hide */
10300 autoSize: Roo.emptyFn,
10304 deferHeight : true,
10307 actionMode : 'wrap',
10312 getAutoCreate : function(){
10314 var align = this.labelAlign || this.parentLabelAlign();
10319 cls: 'form-group' //input-group
10326 type : this.inputType,
10327 cls : 'form-control',
10328 autocomplete: 'new-password',
10329 placeholder : this.placeholder || ''
10333 input.name = this.name;
10336 input.cls += ' input-' + this.size;
10339 if (this.disabled) {
10340 input.disabled=true;
10343 var inputblock = input;
10345 if(this.hasFeedback && !this.allowBlank){
10349 cls: 'glyphicon form-control-feedback'
10352 if(this.removable && !this.editable && !this.tickable){
10354 cls : 'has-feedback',
10360 cls : 'roo-combo-removable-btn close'
10367 cls : 'has-feedback',
10376 if(this.removable && !this.editable && !this.tickable){
10378 cls : 'roo-removable',
10384 cls : 'roo-combo-removable-btn close'
10391 if (this.before || this.after) {
10394 cls : 'input-group',
10398 inputblock.cn.push({
10400 cls : 'input-group-addon input-group-prepend input-group-text',
10405 inputblock.cn.push(input);
10407 if(this.hasFeedback && !this.allowBlank){
10408 inputblock.cls += ' has-feedback';
10409 inputblock.cn.push(feedback);
10413 inputblock.cn.push({
10415 cls : 'input-group-addon input-group-append input-group-text',
10424 var ibwrap = inputblock;
10429 cls: 'roo-select2-choices',
10433 cls: 'roo-select2-search-field',
10445 cls: 'roo-select2-container input-group',
10450 cls: 'form-hidden-field'
10456 if(!this.multiple && this.showToggleBtn){
10462 if (this.caret != false) {
10465 cls: 'fa fa-' + this.caret
10472 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10477 cls: 'combobox-clear',
10491 combobox.cls += ' roo-select2-container-multi';
10495 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10496 tooltip : 'This field is required'
10498 if (Roo.bootstrap.version == 4) {
10501 style : 'display:none'
10506 if (align ==='left' && this.fieldLabel.length) {
10508 cfg.cls += ' roo-form-group-label-left row';
10515 cls : 'control-label',
10516 html : this.fieldLabel
10528 var labelCfg = cfg.cn[1];
10529 var contentCfg = cfg.cn[2];
10531 if(this.indicatorpos == 'right'){
10536 cls : 'control-label',
10540 html : this.fieldLabel
10554 labelCfg = cfg.cn[0];
10555 contentCfg = cfg.cn[1];
10558 if(this.labelWidth > 12){
10559 labelCfg.style = "width: " + this.labelWidth + 'px';
10562 if(this.labelWidth < 13 && this.labelmd == 0){
10563 this.labelmd = this.labelWidth;
10566 if(this.labellg > 0){
10567 labelCfg.cls += ' col-lg-' + this.labellg;
10568 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10571 if(this.labelmd > 0){
10572 labelCfg.cls += ' col-md-' + this.labelmd;
10573 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10576 if(this.labelsm > 0){
10577 labelCfg.cls += ' col-sm-' + this.labelsm;
10578 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10581 if(this.labelxs > 0){
10582 labelCfg.cls += ' col-xs-' + this.labelxs;
10583 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10586 } else if ( this.fieldLabel.length) {
10587 // Roo.log(" label");
10592 //cls : 'input-group-addon',
10593 html : this.fieldLabel
10601 if(this.indicatorpos == 'right'){
10609 html : this.fieldLabel
10623 // Roo.log(" no label && no align");
10630 ['xs','sm','md','lg'].map(function(size){
10631 if (settings[size]) {
10632 cfg.cls += ' col-' + size + '-' + settings[size];
10643 onResize : function(w, h){
10644 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10645 // if(typeof w == 'number'){
10646 // var x = w - this.trigger.getWidth();
10647 // this.inputEl().setWidth(this.adjustWidth('input', x));
10648 // this.trigger.setStyle('left', x+'px');
10653 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10656 getResizeEl : function(){
10657 return this.inputEl();
10661 getPositionEl : function(){
10662 return this.inputEl();
10666 alignErrorIcon : function(){
10667 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10671 initEvents : function(){
10675 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10676 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10677 if(!this.multiple && this.showToggleBtn){
10678 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10679 if(this.hideTrigger){
10680 this.trigger.setDisplayed(false);
10682 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10686 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10689 if(this.removable && !this.editable && !this.tickable){
10690 var close = this.closeTriggerEl();
10693 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10694 close.on('click', this.removeBtnClick, this, close);
10698 //this.trigger.addClassOnOver('x-form-trigger-over');
10699 //this.trigger.addClassOnClick('x-form-trigger-click');
10702 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10706 closeTriggerEl : function()
10708 var close = this.el.select('.roo-combo-removable-btn', true).first();
10709 return close ? close : false;
10712 removeBtnClick : function(e, h, el)
10714 e.preventDefault();
10716 if(this.fireEvent("remove", this) !== false){
10718 this.fireEvent("afterremove", this)
10722 createList : function()
10724 this.list = Roo.get(document.body).createChild({
10725 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10726 cls: 'typeahead typeahead-long dropdown-menu',
10727 style: 'display:none'
10730 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10735 initTrigger : function(){
10740 onDestroy : function(){
10742 this.trigger.removeAllListeners();
10743 // this.trigger.remove();
10746 // this.wrap.remove();
10748 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10752 onFocus : function(){
10753 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10755 if(!this.mimicing){
10756 this.wrap.addClass('x-trigger-wrap-focus');
10757 this.mimicing = true;
10758 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10759 if(this.monitorTab){
10760 this.el.on("keydown", this.checkTab, this);
10767 checkTab : function(e){
10768 if(e.getKey() == e.TAB){
10769 this.triggerBlur();
10774 onBlur : function(){
10779 mimicBlur : function(e, t){
10781 if(!this.wrap.contains(t) && this.validateBlur()){
10782 this.triggerBlur();
10788 triggerBlur : function(){
10789 this.mimicing = false;
10790 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10791 if(this.monitorTab){
10792 this.el.un("keydown", this.checkTab, this);
10794 //this.wrap.removeClass('x-trigger-wrap-focus');
10795 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10799 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10800 validateBlur : function(e, t){
10805 onDisable : function(){
10806 this.inputEl().dom.disabled = true;
10807 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10809 // this.wrap.addClass('x-item-disabled');
10814 onEnable : function(){
10815 this.inputEl().dom.disabled = false;
10816 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10818 // this.el.removeClass('x-item-disabled');
10823 onShow : function(){
10824 var ae = this.getActionEl();
10827 ae.dom.style.display = '';
10828 ae.dom.style.visibility = 'visible';
10834 onHide : function(){
10835 var ae = this.getActionEl();
10836 ae.dom.style.display = 'none';
10840 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10841 * by an implementing function.
10843 * @param {EventObject} e
10845 onTriggerClick : Roo.emptyFn
10849 * Ext JS Library 1.1.1
10850 * Copyright(c) 2006-2007, Ext JS, LLC.
10852 * Originally Released Under LGPL - original licence link has changed is not relivant.
10855 * <script type="text/javascript">
10860 * @class Roo.data.SortTypes
10862 * Defines the default sorting (casting?) comparison functions used when sorting data.
10864 Roo.data.SortTypes = {
10866 * Default sort that does nothing
10867 * @param {Mixed} s The value being converted
10868 * @return {Mixed} The comparison value
10870 none : function(s){
10875 * The regular expression used to strip tags
10879 stripTagsRE : /<\/?[^>]+>/gi,
10882 * Strips all HTML tags to sort on text only
10883 * @param {Mixed} s The value being converted
10884 * @return {String} The comparison value
10886 asText : function(s){
10887 return String(s).replace(this.stripTagsRE, "");
10891 * Strips all HTML tags to sort on text only - Case insensitive
10892 * @param {Mixed} s The value being converted
10893 * @return {String} The comparison value
10895 asUCText : function(s){
10896 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10900 * Case insensitive string
10901 * @param {Mixed} s The value being converted
10902 * @return {String} The comparison value
10904 asUCString : function(s) {
10905 return String(s).toUpperCase();
10910 * @param {Mixed} s The value being converted
10911 * @return {Number} The comparison value
10913 asDate : function(s) {
10917 if(s instanceof Date){
10918 return s.getTime();
10920 return Date.parse(String(s));
10925 * @param {Mixed} s The value being converted
10926 * @return {Float} The comparison value
10928 asFloat : function(s) {
10929 var val = parseFloat(String(s).replace(/,/g, ""));
10938 * @param {Mixed} s The value being converted
10939 * @return {Number} The comparison value
10941 asInt : function(s) {
10942 var val = parseInt(String(s).replace(/,/g, ""));
10950 * Ext JS Library 1.1.1
10951 * Copyright(c) 2006-2007, Ext JS, LLC.
10953 * Originally Released Under LGPL - original licence link has changed is not relivant.
10956 * <script type="text/javascript">
10960 * @class Roo.data.Record
10961 * Instances of this class encapsulate both record <em>definition</em> information, and record
10962 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10963 * to access Records cached in an {@link Roo.data.Store} object.<br>
10965 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10966 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10969 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10971 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10972 * {@link #create}. The parameters are the same.
10973 * @param {Array} data An associative Array of data values keyed by the field name.
10974 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10975 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10976 * not specified an integer id is generated.
10978 Roo.data.Record = function(data, id){
10979 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10984 * Generate a constructor for a specific record layout.
10985 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10986 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10987 * Each field definition object may contain the following properties: <ul>
10988 * <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,
10989 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10990 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10991 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10992 * is being used, then this is a string containing the javascript expression to reference the data relative to
10993 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10994 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10995 * this may be omitted.</p></li>
10996 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10997 * <ul><li>auto (Default, implies no conversion)</li>
11002 * <li>date</li></ul></p></li>
11003 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11004 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11005 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11006 * by the Reader into an object that will be stored in the Record. It is passed the
11007 * following parameters:<ul>
11008 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11010 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11012 * <br>usage:<br><pre><code>
11013 var TopicRecord = Roo.data.Record.create(
11014 {name: 'title', mapping: 'topic_title'},
11015 {name: 'author', mapping: 'username'},
11016 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11017 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11018 {name: 'lastPoster', mapping: 'user2'},
11019 {name: 'excerpt', mapping: 'post_text'}
11022 var myNewRecord = new TopicRecord({
11023 title: 'Do my job please',
11026 lastPost: new Date(),
11027 lastPoster: 'Animal',
11028 excerpt: 'No way dude!'
11030 myStore.add(myNewRecord);
11035 Roo.data.Record.create = function(o){
11036 var f = function(){
11037 f.superclass.constructor.apply(this, arguments);
11039 Roo.extend(f, Roo.data.Record);
11040 var p = f.prototype;
11041 p.fields = new Roo.util.MixedCollection(false, function(field){
11044 for(var i = 0, len = o.length; i < len; i++){
11045 p.fields.add(new Roo.data.Field(o[i]));
11047 f.getField = function(name){
11048 return p.fields.get(name);
11053 Roo.data.Record.AUTO_ID = 1000;
11054 Roo.data.Record.EDIT = 'edit';
11055 Roo.data.Record.REJECT = 'reject';
11056 Roo.data.Record.COMMIT = 'commit';
11058 Roo.data.Record.prototype = {
11060 * Readonly flag - true if this record has been modified.
11069 join : function(store){
11070 this.store = store;
11074 * Set the named field to the specified value.
11075 * @param {String} name The name of the field to set.
11076 * @param {Object} value The value to set the field to.
11078 set : function(name, value){
11079 if(this.data[name] == value){
11083 if(!this.modified){
11084 this.modified = {};
11086 if(typeof this.modified[name] == 'undefined'){
11087 this.modified[name] = this.data[name];
11089 this.data[name] = value;
11090 if(!this.editing && this.store){
11091 this.store.afterEdit(this);
11096 * Get the value of the named field.
11097 * @param {String} name The name of the field to get the value of.
11098 * @return {Object} The value of the field.
11100 get : function(name){
11101 return this.data[name];
11105 beginEdit : function(){
11106 this.editing = true;
11107 this.modified = {};
11111 cancelEdit : function(){
11112 this.editing = false;
11113 delete this.modified;
11117 endEdit : function(){
11118 this.editing = false;
11119 if(this.dirty && this.store){
11120 this.store.afterEdit(this);
11125 * Usually called by the {@link Roo.data.Store} which owns the Record.
11126 * Rejects all changes made to the Record since either creation, or the last commit operation.
11127 * Modified fields are reverted to their original values.
11129 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11130 * of reject operations.
11132 reject : function(){
11133 var m = this.modified;
11135 if(typeof m[n] != "function"){
11136 this.data[n] = m[n];
11139 this.dirty = false;
11140 delete this.modified;
11141 this.editing = false;
11143 this.store.afterReject(this);
11148 * Usually called by the {@link Roo.data.Store} which owns the Record.
11149 * Commits all changes made to the Record since either creation, or the last commit operation.
11151 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11152 * of commit operations.
11154 commit : function(){
11155 this.dirty = false;
11156 delete this.modified;
11157 this.editing = false;
11159 this.store.afterCommit(this);
11164 hasError : function(){
11165 return this.error != null;
11169 clearError : function(){
11174 * Creates a copy of this record.
11175 * @param {String} id (optional) A new record id if you don't want to use this record's id
11178 copy : function(newId) {
11179 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11183 * Ext JS Library 1.1.1
11184 * Copyright(c) 2006-2007, Ext JS, LLC.
11186 * Originally Released Under LGPL - original licence link has changed is not relivant.
11189 * <script type="text/javascript">
11195 * @class Roo.data.Store
11196 * @extends Roo.util.Observable
11197 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11198 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11200 * 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
11201 * has no knowledge of the format of the data returned by the Proxy.<br>
11203 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11204 * instances from the data object. These records are cached and made available through accessor functions.
11206 * Creates a new Store.
11207 * @param {Object} config A config object containing the objects needed for the Store to access data,
11208 * and read the data into Records.
11210 Roo.data.Store = function(config){
11211 this.data = new Roo.util.MixedCollection(false);
11212 this.data.getKey = function(o){
11215 this.baseParams = {};
11217 this.paramNames = {
11222 "multisort" : "_multisort"
11225 if(config && config.data){
11226 this.inlineData = config.data;
11227 delete config.data;
11230 Roo.apply(this, config);
11232 if(this.reader){ // reader passed
11233 this.reader = Roo.factory(this.reader, Roo.data);
11234 this.reader.xmodule = this.xmodule || false;
11235 if(!this.recordType){
11236 this.recordType = this.reader.recordType;
11238 if(this.reader.onMetaChange){
11239 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11243 if(this.recordType){
11244 this.fields = this.recordType.prototype.fields;
11246 this.modified = [];
11250 * @event datachanged
11251 * Fires when the data cache has changed, and a widget which is using this Store
11252 * as a Record cache should refresh its view.
11253 * @param {Store} this
11255 datachanged : true,
11257 * @event metachange
11258 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11259 * @param {Store} this
11260 * @param {Object} meta The JSON metadata
11265 * Fires when Records have been added to the Store
11266 * @param {Store} this
11267 * @param {Roo.data.Record[]} records The array of Records added
11268 * @param {Number} index The index at which the record(s) were added
11273 * Fires when a Record has been removed from the Store
11274 * @param {Store} this
11275 * @param {Roo.data.Record} record The Record that was removed
11276 * @param {Number} index The index at which the record was removed
11281 * Fires when a Record has been updated
11282 * @param {Store} this
11283 * @param {Roo.data.Record} record The Record that was updated
11284 * @param {String} operation The update operation being performed. Value may be one of:
11286 Roo.data.Record.EDIT
11287 Roo.data.Record.REJECT
11288 Roo.data.Record.COMMIT
11294 * Fires when the data cache has been cleared.
11295 * @param {Store} this
11299 * @event beforeload
11300 * Fires before a request is made for a new data object. If the beforeload handler returns false
11301 * the load action will be canceled.
11302 * @param {Store} this
11303 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11307 * @event beforeloadadd
11308 * Fires after a new set of Records has been loaded.
11309 * @param {Store} this
11310 * @param {Roo.data.Record[]} records The Records that were loaded
11311 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11313 beforeloadadd : true,
11316 * Fires after a new set of Records has been loaded, before they are added to the store.
11317 * @param {Store} this
11318 * @param {Roo.data.Record[]} records The Records that were loaded
11319 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11320 * @params {Object} return from reader
11324 * @event loadexception
11325 * Fires if an exception occurs in the Proxy during loading.
11326 * Called with the signature of the Proxy's "loadexception" event.
11327 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11330 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11331 * @param {Object} load options
11332 * @param {Object} jsonData from your request (normally this contains the Exception)
11334 loadexception : true
11338 this.proxy = Roo.factory(this.proxy, Roo.data);
11339 this.proxy.xmodule = this.xmodule || false;
11340 this.relayEvents(this.proxy, ["loadexception"]);
11342 this.sortToggle = {};
11343 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11345 Roo.data.Store.superclass.constructor.call(this);
11347 if(this.inlineData){
11348 this.loadData(this.inlineData);
11349 delete this.inlineData;
11353 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11355 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11356 * without a remote query - used by combo/forms at present.
11360 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11363 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11366 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11367 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11370 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11371 * on any HTTP request
11374 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11377 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11381 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11382 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11384 remoteSort : false,
11387 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11388 * loaded or when a record is removed. (defaults to false).
11390 pruneModifiedRecords : false,
11393 lastOptions : null,
11396 * Add Records to the Store and fires the add event.
11397 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11399 add : function(records){
11400 records = [].concat(records);
11401 for(var i = 0, len = records.length; i < len; i++){
11402 records[i].join(this);
11404 var index = this.data.length;
11405 this.data.addAll(records);
11406 this.fireEvent("add", this, records, index);
11410 * Remove a Record from the Store and fires the remove event.
11411 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11413 remove : function(record){
11414 var index = this.data.indexOf(record);
11415 this.data.removeAt(index);
11417 if(this.pruneModifiedRecords){
11418 this.modified.remove(record);
11420 this.fireEvent("remove", this, record, index);
11424 * Remove all Records from the Store and fires the clear event.
11426 removeAll : function(){
11428 if(this.pruneModifiedRecords){
11429 this.modified = [];
11431 this.fireEvent("clear", this);
11435 * Inserts Records to the Store at the given index and fires the add event.
11436 * @param {Number} index The start index at which to insert the passed Records.
11437 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11439 insert : function(index, records){
11440 records = [].concat(records);
11441 for(var i = 0, len = records.length; i < len; i++){
11442 this.data.insert(index, records[i]);
11443 records[i].join(this);
11445 this.fireEvent("add", this, records, index);
11449 * Get the index within the cache of the passed Record.
11450 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11451 * @return {Number} The index of the passed Record. Returns -1 if not found.
11453 indexOf : function(record){
11454 return this.data.indexOf(record);
11458 * Get the index within the cache of the Record with the passed id.
11459 * @param {String} id The id of the Record to find.
11460 * @return {Number} The index of the Record. Returns -1 if not found.
11462 indexOfId : function(id){
11463 return this.data.indexOfKey(id);
11467 * Get the Record with the specified id.
11468 * @param {String} id The id of the Record to find.
11469 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11471 getById : function(id){
11472 return this.data.key(id);
11476 * Get the Record at the specified index.
11477 * @param {Number} index The index of the Record to find.
11478 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11480 getAt : function(index){
11481 return this.data.itemAt(index);
11485 * Returns a range of Records between specified indices.
11486 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11487 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11488 * @return {Roo.data.Record[]} An array of Records
11490 getRange : function(start, end){
11491 return this.data.getRange(start, end);
11495 storeOptions : function(o){
11496 o = Roo.apply({}, o);
11499 this.lastOptions = o;
11503 * Loads the Record cache from the configured Proxy using the configured Reader.
11505 * If using remote paging, then the first load call must specify the <em>start</em>
11506 * and <em>limit</em> properties in the options.params property to establish the initial
11507 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11509 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11510 * and this call will return before the new data has been loaded. Perform any post-processing
11511 * in a callback function, or in a "load" event handler.</strong>
11513 * @param {Object} options An object containing properties which control loading options:<ul>
11514 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11515 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11516 * passed the following arguments:<ul>
11517 * <li>r : Roo.data.Record[]</li>
11518 * <li>options: Options object from the load call</li>
11519 * <li>success: Boolean success indicator</li></ul></li>
11520 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11521 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11524 load : function(options){
11525 options = options || {};
11526 if(this.fireEvent("beforeload", this, options) !== false){
11527 this.storeOptions(options);
11528 var p = Roo.apply(options.params || {}, this.baseParams);
11529 // if meta was not loaded from remote source.. try requesting it.
11530 if (!this.reader.metaFromRemote) {
11531 p._requestMeta = 1;
11533 if(this.sortInfo && this.remoteSort){
11534 var pn = this.paramNames;
11535 p[pn["sort"]] = this.sortInfo.field;
11536 p[pn["dir"]] = this.sortInfo.direction;
11538 if (this.multiSort) {
11539 var pn = this.paramNames;
11540 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11543 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11548 * Reloads the Record cache from the configured Proxy using the configured Reader and
11549 * the options from the last load operation performed.
11550 * @param {Object} options (optional) An object containing properties which may override the options
11551 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11552 * the most recently used options are reused).
11554 reload : function(options){
11555 this.load(Roo.applyIf(options||{}, this.lastOptions));
11559 // Called as a callback by the Reader during a load operation.
11560 loadRecords : function(o, options, success){
11561 if(!o || success === false){
11562 if(success !== false){
11563 this.fireEvent("load", this, [], options, o);
11565 if(options.callback){
11566 options.callback.call(options.scope || this, [], options, false);
11570 // if data returned failure - throw an exception.
11571 if (o.success === false) {
11572 // show a message if no listener is registered.
11573 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11574 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11576 // loadmask wil be hooked into this..
11577 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11580 var r = o.records, t = o.totalRecords || r.length;
11582 this.fireEvent("beforeloadadd", this, r, options, o);
11584 if(!options || options.add !== true){
11585 if(this.pruneModifiedRecords){
11586 this.modified = [];
11588 for(var i = 0, len = r.length; i < len; i++){
11592 this.data = this.snapshot;
11593 delete this.snapshot;
11596 this.data.addAll(r);
11597 this.totalLength = t;
11599 this.fireEvent("datachanged", this);
11601 this.totalLength = Math.max(t, this.data.length+r.length);
11605 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11607 var e = new Roo.data.Record({});
11609 e.set(this.parent.displayField, this.parent.emptyTitle);
11610 e.set(this.parent.valueField, '');
11615 this.fireEvent("load", this, r, options, o);
11616 if(options.callback){
11617 options.callback.call(options.scope || this, r, options, true);
11623 * Loads data from a passed data block. A Reader which understands the format of the data
11624 * must have been configured in the constructor.
11625 * @param {Object} data The data block from which to read the Records. The format of the data expected
11626 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11627 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11629 loadData : function(o, append){
11630 var r = this.reader.readRecords(o);
11631 this.loadRecords(r, {add: append}, true);
11635 * Gets the number of cached records.
11637 * <em>If using paging, this may not be the total size of the dataset. If the data object
11638 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11639 * the data set size</em>
11641 getCount : function(){
11642 return this.data.length || 0;
11646 * Gets the total number of records in the dataset as returned by the server.
11648 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11649 * the dataset size</em>
11651 getTotalCount : function(){
11652 return this.totalLength || 0;
11656 * Returns the sort state of the Store as an object with two properties:
11658 field {String} The name of the field by which the Records are sorted
11659 direction {String} The sort order, "ASC" or "DESC"
11662 getSortState : function(){
11663 return this.sortInfo;
11667 applySort : function(){
11668 if(this.sortInfo && !this.remoteSort){
11669 var s = this.sortInfo, f = s.field;
11670 var st = this.fields.get(f).sortType;
11671 var fn = function(r1, r2){
11672 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11673 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11675 this.data.sort(s.direction, fn);
11676 if(this.snapshot && this.snapshot != this.data){
11677 this.snapshot.sort(s.direction, fn);
11683 * Sets the default sort column and order to be used by the next load operation.
11684 * @param {String} fieldName The name of the field to sort by.
11685 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11687 setDefaultSort : function(field, dir){
11688 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11692 * Sort the Records.
11693 * If remote sorting is used, the sort is performed on the server, and the cache is
11694 * reloaded. If local sorting is used, the cache is sorted internally.
11695 * @param {String} fieldName The name of the field to sort by.
11696 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11698 sort : function(fieldName, dir){
11699 var f = this.fields.get(fieldName);
11701 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11703 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11704 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11709 this.sortToggle[f.name] = dir;
11710 this.sortInfo = {field: f.name, direction: dir};
11711 if(!this.remoteSort){
11713 this.fireEvent("datachanged", this);
11715 this.load(this.lastOptions);
11720 * Calls the specified function for each of the Records in the cache.
11721 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11722 * Returning <em>false</em> aborts and exits the iteration.
11723 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11725 each : function(fn, scope){
11726 this.data.each(fn, scope);
11730 * Gets all records modified since the last commit. Modified records are persisted across load operations
11731 * (e.g., during paging).
11732 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11734 getModifiedRecords : function(){
11735 return this.modified;
11739 createFilterFn : function(property, value, anyMatch){
11740 if(!value.exec){ // not a regex
11741 value = String(value);
11742 if(value.length == 0){
11745 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11747 return function(r){
11748 return value.test(r.data[property]);
11753 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11754 * @param {String} property A field on your records
11755 * @param {Number} start The record index to start at (defaults to 0)
11756 * @param {Number} end The last record index to include (defaults to length - 1)
11757 * @return {Number} The sum
11759 sum : function(property, start, end){
11760 var rs = this.data.items, v = 0;
11761 start = start || 0;
11762 end = (end || end === 0) ? end : rs.length-1;
11764 for(var i = start; i <= end; i++){
11765 v += (rs[i].data[property] || 0);
11771 * Filter the records by a specified property.
11772 * @param {String} field A field on your records
11773 * @param {String/RegExp} value Either a string that the field
11774 * should start with or a RegExp to test against the field
11775 * @param {Boolean} anyMatch True to match any part not just the beginning
11777 filter : function(property, value, anyMatch){
11778 var fn = this.createFilterFn(property, value, anyMatch);
11779 return fn ? this.filterBy(fn) : this.clearFilter();
11783 * Filter by a function. The specified function will be called with each
11784 * record in this data source. If the function returns true the record is included,
11785 * otherwise it is filtered.
11786 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11787 * @param {Object} scope (optional) The scope of the function (defaults to this)
11789 filterBy : function(fn, scope){
11790 this.snapshot = this.snapshot || this.data;
11791 this.data = this.queryBy(fn, scope||this);
11792 this.fireEvent("datachanged", this);
11796 * Query the records by a specified property.
11797 * @param {String} field A field on your records
11798 * @param {String/RegExp} value Either a string that the field
11799 * should start with or a RegExp to test against the field
11800 * @param {Boolean} anyMatch True to match any part not just the beginning
11801 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11803 query : function(property, value, anyMatch){
11804 var fn = this.createFilterFn(property, value, anyMatch);
11805 return fn ? this.queryBy(fn) : this.data.clone();
11809 * Query by a function. The specified function will be called with each
11810 * record in this data source. If the function returns true the record is included
11812 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11813 * @param {Object} scope (optional) The scope of the function (defaults to this)
11814 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11816 queryBy : function(fn, scope){
11817 var data = this.snapshot || this.data;
11818 return data.filterBy(fn, scope||this);
11822 * Collects unique values for a particular dataIndex from this store.
11823 * @param {String} dataIndex The property to collect
11824 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11825 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11826 * @return {Array} An array of the unique values
11828 collect : function(dataIndex, allowNull, bypassFilter){
11829 var d = (bypassFilter === true && this.snapshot) ?
11830 this.snapshot.items : this.data.items;
11831 var v, sv, r = [], l = {};
11832 for(var i = 0, len = d.length; i < len; i++){
11833 v = d[i].data[dataIndex];
11835 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11844 * Revert to a view of the Record cache with no filtering applied.
11845 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11847 clearFilter : function(suppressEvent){
11848 if(this.snapshot && this.snapshot != this.data){
11849 this.data = this.snapshot;
11850 delete this.snapshot;
11851 if(suppressEvent !== true){
11852 this.fireEvent("datachanged", this);
11858 afterEdit : function(record){
11859 if(this.modified.indexOf(record) == -1){
11860 this.modified.push(record);
11862 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11866 afterReject : function(record){
11867 this.modified.remove(record);
11868 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11872 afterCommit : function(record){
11873 this.modified.remove(record);
11874 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11878 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11879 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11881 commitChanges : function(){
11882 var m = this.modified.slice(0);
11883 this.modified = [];
11884 for(var i = 0, len = m.length; i < len; i++){
11890 * Cancel outstanding changes on all changed records.
11892 rejectChanges : function(){
11893 var m = this.modified.slice(0);
11894 this.modified = [];
11895 for(var i = 0, len = m.length; i < len; i++){
11900 onMetaChange : function(meta, rtype, o){
11901 this.recordType = rtype;
11902 this.fields = rtype.prototype.fields;
11903 delete this.snapshot;
11904 this.sortInfo = meta.sortInfo || this.sortInfo;
11905 this.modified = [];
11906 this.fireEvent('metachange', this, this.reader.meta);
11909 moveIndex : function(data, type)
11911 var index = this.indexOf(data);
11913 var newIndex = index + type;
11917 this.insert(newIndex, data);
11922 * Ext JS Library 1.1.1
11923 * Copyright(c) 2006-2007, Ext JS, LLC.
11925 * Originally Released Under LGPL - original licence link has changed is not relivant.
11928 * <script type="text/javascript">
11932 * @class Roo.data.SimpleStore
11933 * @extends Roo.data.Store
11934 * Small helper class to make creating Stores from Array data easier.
11935 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11936 * @cfg {Array} fields An array of field definition objects, or field name strings.
11937 * @cfg {Array} data The multi-dimensional array of data
11939 * @param {Object} config
11941 Roo.data.SimpleStore = function(config){
11942 Roo.data.SimpleStore.superclass.constructor.call(this, {
11944 reader: new Roo.data.ArrayReader({
11947 Roo.data.Record.create(config.fields)
11949 proxy : new Roo.data.MemoryProxy(config.data)
11953 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11955 * Ext JS Library 1.1.1
11956 * Copyright(c) 2006-2007, Ext JS, LLC.
11958 * Originally Released Under LGPL - original licence link has changed is not relivant.
11961 * <script type="text/javascript">
11966 * @extends Roo.data.Store
11967 * @class Roo.data.JsonStore
11968 * Small helper class to make creating Stores for JSON data easier. <br/>
11970 var store = new Roo.data.JsonStore({
11971 url: 'get-images.php',
11973 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11976 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11977 * JsonReader and HttpProxy (unless inline data is provided).</b>
11978 * @cfg {Array} fields An array of field definition objects, or field name strings.
11980 * @param {Object} config
11982 Roo.data.JsonStore = function(c){
11983 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11984 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11985 reader: new Roo.data.JsonReader(c, c.fields)
11988 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11990 * Ext JS Library 1.1.1
11991 * Copyright(c) 2006-2007, Ext JS, LLC.
11993 * Originally Released Under LGPL - original licence link has changed is not relivant.
11996 * <script type="text/javascript">
12000 Roo.data.Field = function(config){
12001 if(typeof config == "string"){
12002 config = {name: config};
12004 Roo.apply(this, config);
12007 this.type = "auto";
12010 var st = Roo.data.SortTypes;
12011 // named sortTypes are supported, here we look them up
12012 if(typeof this.sortType == "string"){
12013 this.sortType = st[this.sortType];
12016 // set default sortType for strings and dates
12017 if(!this.sortType){
12020 this.sortType = st.asUCString;
12023 this.sortType = st.asDate;
12026 this.sortType = st.none;
12031 var stripRe = /[\$,%]/g;
12033 // prebuilt conversion function for this field, instead of
12034 // switching every time we're reading a value
12036 var cv, dateFormat = this.dateFormat;
12041 cv = function(v){ return v; };
12044 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12048 return v !== undefined && v !== null && v !== '' ?
12049 parseInt(String(v).replace(stripRe, ""), 10) : '';
12054 return v !== undefined && v !== null && v !== '' ?
12055 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12060 cv = function(v){ return v === true || v === "true" || v == 1; };
12067 if(v instanceof Date){
12071 if(dateFormat == "timestamp"){
12072 return new Date(v*1000);
12074 return Date.parseDate(v, dateFormat);
12076 var parsed = Date.parse(v);
12077 return parsed ? new Date(parsed) : null;
12086 Roo.data.Field.prototype = {
12094 * Ext JS Library 1.1.1
12095 * Copyright(c) 2006-2007, Ext JS, LLC.
12097 * Originally Released Under LGPL - original licence link has changed is not relivant.
12100 * <script type="text/javascript">
12103 // Base class for reading structured data from a data source. This class is intended to be
12104 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12107 * @class Roo.data.DataReader
12108 * Base class for reading structured data from a data source. This class is intended to be
12109 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12112 Roo.data.DataReader = function(meta, recordType){
12116 this.recordType = recordType instanceof Array ?
12117 Roo.data.Record.create(recordType) : recordType;
12120 Roo.data.DataReader.prototype = {
12122 * Create an empty record
12123 * @param {Object} data (optional) - overlay some values
12124 * @return {Roo.data.Record} record created.
12126 newRow : function(d) {
12128 this.recordType.prototype.fields.each(function(c) {
12130 case 'int' : da[c.name] = 0; break;
12131 case 'date' : da[c.name] = new Date(); break;
12132 case 'float' : da[c.name] = 0.0; break;
12133 case 'boolean' : da[c.name] = false; break;
12134 default : da[c.name] = ""; break;
12138 return new this.recordType(Roo.apply(da, d));
12143 * Ext JS Library 1.1.1
12144 * Copyright(c) 2006-2007, Ext JS, LLC.
12146 * Originally Released Under LGPL - original licence link has changed is not relivant.
12149 * <script type="text/javascript">
12153 * @class Roo.data.DataProxy
12154 * @extends Roo.data.Observable
12155 * This class is an abstract base class for implementations which provide retrieval of
12156 * unformatted data objects.<br>
12158 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12159 * (of the appropriate type which knows how to parse the data object) to provide a block of
12160 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12162 * Custom implementations must implement the load method as described in
12163 * {@link Roo.data.HttpProxy#load}.
12165 Roo.data.DataProxy = function(){
12168 * @event beforeload
12169 * Fires before a network request is made to retrieve a data object.
12170 * @param {Object} This DataProxy object.
12171 * @param {Object} params The params parameter to the load function.
12176 * Fires before the load method's callback is called.
12177 * @param {Object} This DataProxy object.
12178 * @param {Object} o The data object.
12179 * @param {Object} arg The callback argument object passed to the load function.
12183 * @event loadexception
12184 * Fires if an Exception occurs during data retrieval.
12185 * @param {Object} This DataProxy object.
12186 * @param {Object} o The data object.
12187 * @param {Object} arg The callback argument object passed to the load function.
12188 * @param {Object} e The Exception.
12190 loadexception : true
12192 Roo.data.DataProxy.superclass.constructor.call(this);
12195 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12198 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12202 * Ext JS Library 1.1.1
12203 * Copyright(c) 2006-2007, Ext JS, LLC.
12205 * Originally Released Under LGPL - original licence link has changed is not relivant.
12208 * <script type="text/javascript">
12211 * @class Roo.data.MemoryProxy
12212 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12213 * to the Reader when its load method is called.
12215 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12217 Roo.data.MemoryProxy = function(data){
12221 Roo.data.MemoryProxy.superclass.constructor.call(this);
12225 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12228 * Load data from the requested source (in this case an in-memory
12229 * data object passed to the constructor), read the data object into
12230 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12231 * process that block using the passed callback.
12232 * @param {Object} params This parameter is not used by the MemoryProxy class.
12233 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12234 * object into a block of Roo.data.Records.
12235 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12236 * The function must be passed <ul>
12237 * <li>The Record block object</li>
12238 * <li>The "arg" argument from the load function</li>
12239 * <li>A boolean success indicator</li>
12241 * @param {Object} scope The scope in which to call the callback
12242 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12244 load : function(params, reader, callback, scope, arg){
12245 params = params || {};
12248 result = reader.readRecords(this.data);
12250 this.fireEvent("loadexception", this, arg, null, e);
12251 callback.call(scope, null, arg, false);
12254 callback.call(scope, result, arg, true);
12258 update : function(params, records){
12263 * Ext JS Library 1.1.1
12264 * Copyright(c) 2006-2007, Ext JS, LLC.
12266 * Originally Released Under LGPL - original licence link has changed is not relivant.
12269 * <script type="text/javascript">
12272 * @class Roo.data.HttpProxy
12273 * @extends Roo.data.DataProxy
12274 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12275 * configured to reference a certain URL.<br><br>
12277 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12278 * from which the running page was served.<br><br>
12280 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12282 * Be aware that to enable the browser to parse an XML document, the server must set
12283 * the Content-Type header in the HTTP response to "text/xml".
12285 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12286 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12287 * will be used to make the request.
12289 Roo.data.HttpProxy = function(conn){
12290 Roo.data.HttpProxy.superclass.constructor.call(this);
12291 // is conn a conn config or a real conn?
12293 this.useAjax = !conn || !conn.events;
12297 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12298 // thse are take from connection...
12301 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12304 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12305 * extra parameters to each request made by this object. (defaults to undefined)
12308 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12309 * to each request made by this object. (defaults to undefined)
12312 * @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)
12315 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12318 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12324 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12328 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12329 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12330 * a finer-grained basis than the DataProxy events.
12332 getConnection : function(){
12333 return this.useAjax ? Roo.Ajax : this.conn;
12337 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12338 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12339 * process that block using the passed callback.
12340 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12341 * for the request to the remote server.
12342 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12343 * object into a block of Roo.data.Records.
12344 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12345 * The function must be passed <ul>
12346 * <li>The Record block object</li>
12347 * <li>The "arg" argument from the load function</li>
12348 * <li>A boolean success indicator</li>
12350 * @param {Object} scope The scope in which to call the callback
12351 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12353 load : function(params, reader, callback, scope, arg){
12354 if(this.fireEvent("beforeload", this, params) !== false){
12356 params : params || {},
12358 callback : callback,
12363 callback : this.loadResponse,
12367 Roo.applyIf(o, this.conn);
12368 if(this.activeRequest){
12369 Roo.Ajax.abort(this.activeRequest);
12371 this.activeRequest = Roo.Ajax.request(o);
12373 this.conn.request(o);
12376 callback.call(scope||this, null, arg, false);
12381 loadResponse : function(o, success, response){
12382 delete this.activeRequest;
12384 this.fireEvent("loadexception", this, o, response);
12385 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12390 result = o.reader.read(response);
12392 this.fireEvent("loadexception", this, o, response, e);
12393 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12397 this.fireEvent("load", this, o, o.request.arg);
12398 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12402 update : function(dataSet){
12407 updateResponse : function(dataSet){
12412 * Ext JS Library 1.1.1
12413 * Copyright(c) 2006-2007, Ext JS, LLC.
12415 * Originally Released Under LGPL - original licence link has changed is not relivant.
12418 * <script type="text/javascript">
12422 * @class Roo.data.ScriptTagProxy
12423 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12424 * other than the originating domain of the running page.<br><br>
12426 * <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
12427 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12429 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12430 * source code that is used as the source inside a <script> tag.<br><br>
12432 * In order for the browser to process the returned data, the server must wrap the data object
12433 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12434 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12435 * depending on whether the callback name was passed:
12438 boolean scriptTag = false;
12439 String cb = request.getParameter("callback");
12442 response.setContentType("text/javascript");
12444 response.setContentType("application/x-json");
12446 Writer out = response.getWriter();
12448 out.write(cb + "(");
12450 out.print(dataBlock.toJsonString());
12457 * @param {Object} config A configuration object.
12459 Roo.data.ScriptTagProxy = function(config){
12460 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12461 Roo.apply(this, config);
12462 this.head = document.getElementsByTagName("head")[0];
12465 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12467 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12469 * @cfg {String} url The URL from which to request the data object.
12472 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12476 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12477 * the server the name of the callback function set up by the load call to process the returned data object.
12478 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12479 * javascript output which calls this named function passing the data object as its only parameter.
12481 callbackParam : "callback",
12483 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12484 * name to the request.
12489 * Load data from the configured URL, read the data object into
12490 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12491 * process that block using the passed callback.
12492 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12493 * for the request to the remote server.
12494 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12495 * object into a block of Roo.data.Records.
12496 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12497 * The function must be passed <ul>
12498 * <li>The Record block object</li>
12499 * <li>The "arg" argument from the load function</li>
12500 * <li>A boolean success indicator</li>
12502 * @param {Object} scope The scope in which to call the callback
12503 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12505 load : function(params, reader, callback, scope, arg){
12506 if(this.fireEvent("beforeload", this, params) !== false){
12508 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12510 var url = this.url;
12511 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12513 url += "&_dc=" + (new Date().getTime());
12515 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12518 cb : "stcCallback"+transId,
12519 scriptId : "stcScript"+transId,
12523 callback : callback,
12529 window[trans.cb] = function(o){
12530 conn.handleResponse(o, trans);
12533 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12535 if(this.autoAbort !== false){
12539 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12541 var script = document.createElement("script");
12542 script.setAttribute("src", url);
12543 script.setAttribute("type", "text/javascript");
12544 script.setAttribute("id", trans.scriptId);
12545 this.head.appendChild(script);
12547 this.trans = trans;
12549 callback.call(scope||this, null, arg, false);
12554 isLoading : function(){
12555 return this.trans ? true : false;
12559 * Abort the current server request.
12561 abort : function(){
12562 if(this.isLoading()){
12563 this.destroyTrans(this.trans);
12568 destroyTrans : function(trans, isLoaded){
12569 this.head.removeChild(document.getElementById(trans.scriptId));
12570 clearTimeout(trans.timeoutId);
12572 window[trans.cb] = undefined;
12574 delete window[trans.cb];
12577 // if hasn't been loaded, wait for load to remove it to prevent script error
12578 window[trans.cb] = function(){
12579 window[trans.cb] = undefined;
12581 delete window[trans.cb];
12588 handleResponse : function(o, trans){
12589 this.trans = false;
12590 this.destroyTrans(trans, true);
12593 result = trans.reader.readRecords(o);
12595 this.fireEvent("loadexception", this, o, trans.arg, e);
12596 trans.callback.call(trans.scope||window, null, trans.arg, false);
12599 this.fireEvent("load", this, o, trans.arg);
12600 trans.callback.call(trans.scope||window, result, trans.arg, true);
12604 handleFailure : function(trans){
12605 this.trans = false;
12606 this.destroyTrans(trans, false);
12607 this.fireEvent("loadexception", this, null, trans.arg);
12608 trans.callback.call(trans.scope||window, null, trans.arg, false);
12612 * Ext JS Library 1.1.1
12613 * Copyright(c) 2006-2007, Ext JS, LLC.
12615 * Originally Released Under LGPL - original licence link has changed is not relivant.
12618 * <script type="text/javascript">
12622 * @class Roo.data.JsonReader
12623 * @extends Roo.data.DataReader
12624 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12625 * based on mappings in a provided Roo.data.Record constructor.
12627 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12628 * in the reply previously.
12633 var RecordDef = Roo.data.Record.create([
12634 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12635 {name: 'occupation'} // This field will use "occupation" as the mapping.
12637 var myReader = new Roo.data.JsonReader({
12638 totalProperty: "results", // The property which contains the total dataset size (optional)
12639 root: "rows", // The property which contains an Array of row objects
12640 id: "id" // The property within each row object that provides an ID for the record (optional)
12644 * This would consume a JSON file like this:
12646 { 'results': 2, 'rows': [
12647 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12648 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12651 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12652 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12653 * paged from the remote server.
12654 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12655 * @cfg {String} root name of the property which contains the Array of row objects.
12656 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12657 * @cfg {Array} fields Array of field definition objects
12659 * Create a new JsonReader
12660 * @param {Object} meta Metadata configuration options
12661 * @param {Object} recordType Either an Array of field definition objects,
12662 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12664 Roo.data.JsonReader = function(meta, recordType){
12667 // set some defaults:
12668 Roo.applyIf(meta, {
12669 totalProperty: 'total',
12670 successProperty : 'success',
12675 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12677 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12680 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12681 * Used by Store query builder to append _requestMeta to params.
12684 metaFromRemote : false,
12686 * This method is only used by a DataProxy which has retrieved data from a remote server.
12687 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12688 * @return {Object} data A data block which is used by an Roo.data.Store object as
12689 * a cache of Roo.data.Records.
12691 read : function(response){
12692 var json = response.responseText;
12694 var o = /* eval:var:o */ eval("("+json+")");
12696 throw {message: "JsonReader.read: Json object not found"};
12702 this.metaFromRemote = true;
12703 this.meta = o.metaData;
12704 this.recordType = Roo.data.Record.create(o.metaData.fields);
12705 this.onMetaChange(this.meta, this.recordType, o);
12707 return this.readRecords(o);
12710 // private function a store will implement
12711 onMetaChange : function(meta, recordType, o){
12718 simpleAccess: function(obj, subsc) {
12725 getJsonAccessor: function(){
12727 return function(expr) {
12729 return(re.test(expr))
12730 ? new Function("obj", "return obj." + expr)
12735 return Roo.emptyFn;
12740 * Create a data block containing Roo.data.Records from an XML document.
12741 * @param {Object} o An object which contains an Array of row objects in the property specified
12742 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12743 * which contains the total size of the dataset.
12744 * @return {Object} data A data block which is used by an Roo.data.Store object as
12745 * a cache of Roo.data.Records.
12747 readRecords : function(o){
12749 * After any data loads, the raw JSON data is available for further custom processing.
12753 var s = this.meta, Record = this.recordType,
12754 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12756 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12758 if(s.totalProperty) {
12759 this.getTotal = this.getJsonAccessor(s.totalProperty);
12761 if(s.successProperty) {
12762 this.getSuccess = this.getJsonAccessor(s.successProperty);
12764 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12766 var g = this.getJsonAccessor(s.id);
12767 this.getId = function(rec) {
12769 return (r === undefined || r === "") ? null : r;
12772 this.getId = function(){return null;};
12775 for(var jj = 0; jj < fl; jj++){
12777 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12778 this.ef[jj] = this.getJsonAccessor(map);
12782 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12783 if(s.totalProperty){
12784 var vt = parseInt(this.getTotal(o), 10);
12789 if(s.successProperty){
12790 var vs = this.getSuccess(o);
12791 if(vs === false || vs === 'false'){
12796 for(var i = 0; i < c; i++){
12799 var id = this.getId(n);
12800 for(var j = 0; j < fl; j++){
12802 var v = this.ef[j](n);
12804 Roo.log('missing convert for ' + f.name);
12808 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12810 var record = new Record(values, id);
12812 records[i] = record;
12818 totalRecords : totalRecords
12823 * Ext JS Library 1.1.1
12824 * Copyright(c) 2006-2007, Ext JS, LLC.
12826 * Originally Released Under LGPL - original licence link has changed is not relivant.
12829 * <script type="text/javascript">
12833 * @class Roo.data.ArrayReader
12834 * @extends Roo.data.DataReader
12835 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12836 * Each element of that Array represents a row of data fields. The
12837 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12838 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12842 var RecordDef = Roo.data.Record.create([
12843 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12844 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12846 var myReader = new Roo.data.ArrayReader({
12847 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12851 * This would consume an Array like this:
12853 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12855 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12857 * Create a new JsonReader
12858 * @param {Object} meta Metadata configuration options.
12859 * @param {Object} recordType Either an Array of field definition objects
12860 * as specified to {@link Roo.data.Record#create},
12861 * or an {@link Roo.data.Record} object
12862 * created using {@link Roo.data.Record#create}.
12864 Roo.data.ArrayReader = function(meta, recordType){
12865 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12868 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12870 * Create a data block containing Roo.data.Records from an XML document.
12871 * @param {Object} o An Array of row objects which represents the dataset.
12872 * @return {Object} data A data block which is used by an Roo.data.Store object as
12873 * a cache of Roo.data.Records.
12875 readRecords : function(o){
12876 var sid = this.meta ? this.meta.id : null;
12877 var recordType = this.recordType, fields = recordType.prototype.fields;
12880 for(var i = 0; i < root.length; i++){
12883 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12884 for(var j = 0, jlen = fields.length; j < jlen; j++){
12885 var f = fields.items[j];
12886 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12887 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12889 values[f.name] = v;
12891 var record = new recordType(values, id);
12893 records[records.length] = record;
12897 totalRecords : records.length
12906 * @class Roo.bootstrap.ComboBox
12907 * @extends Roo.bootstrap.TriggerField
12908 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12909 * @cfg {Boolean} append (true|false) default false
12910 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12911 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12912 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12913 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12914 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12915 * @cfg {Boolean} animate default true
12916 * @cfg {Boolean} emptyResultText only for touch device
12917 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12918 * @cfg {String} emptyTitle default ''
12920 * Create a new ComboBox.
12921 * @param {Object} config Configuration options
12923 Roo.bootstrap.ComboBox = function(config){
12924 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12928 * Fires when the dropdown list is expanded
12929 * @param {Roo.bootstrap.ComboBox} combo This combo box
12934 * Fires when the dropdown list is collapsed
12935 * @param {Roo.bootstrap.ComboBox} combo This combo box
12939 * @event beforeselect
12940 * Fires before a list item is selected. Return false to cancel the selection.
12941 * @param {Roo.bootstrap.ComboBox} combo This combo box
12942 * @param {Roo.data.Record} record The data record returned from the underlying store
12943 * @param {Number} index The index of the selected item in the dropdown list
12945 'beforeselect' : true,
12948 * Fires when a list item is selected
12949 * @param {Roo.bootstrap.ComboBox} combo This combo box
12950 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12951 * @param {Number} index The index of the selected item in the dropdown list
12955 * @event beforequery
12956 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12957 * The event object passed has these properties:
12958 * @param {Roo.bootstrap.ComboBox} combo This combo box
12959 * @param {String} query The query
12960 * @param {Boolean} forceAll true to force "all" query
12961 * @param {Boolean} cancel true to cancel the query
12962 * @param {Object} e The query event object
12964 'beforequery': true,
12967 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12968 * @param {Roo.bootstrap.ComboBox} combo This combo box
12973 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12974 * @param {Roo.bootstrap.ComboBox} combo This combo box
12975 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12980 * Fires when the remove value from the combobox array
12981 * @param {Roo.bootstrap.ComboBox} combo This combo box
12985 * @event afterremove
12986 * Fires when the remove value from the combobox array
12987 * @param {Roo.bootstrap.ComboBox} combo This combo box
12989 'afterremove' : true,
12991 * @event specialfilter
12992 * Fires when specialfilter
12993 * @param {Roo.bootstrap.ComboBox} combo This combo box
12995 'specialfilter' : true,
12998 * Fires when tick the element
12999 * @param {Roo.bootstrap.ComboBox} combo This combo box
13003 * @event touchviewdisplay
13004 * Fires when touch view require special display (default is using displayField)
13005 * @param {Roo.bootstrap.ComboBox} combo This combo box
13006 * @param {Object} cfg set html .
13008 'touchviewdisplay' : true
13013 this.tickItems = [];
13015 this.selectedIndex = -1;
13016 if(this.mode == 'local'){
13017 if(config.queryDelay === undefined){
13018 this.queryDelay = 10;
13020 if(config.minChars === undefined){
13026 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13029 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13030 * rendering into an Roo.Editor, defaults to false)
13033 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13034 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13037 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13040 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13041 * the dropdown list (defaults to undefined, with no header element)
13045 * @cfg {String/Roo.Template} tpl The template to use to render the output
13049 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13051 listWidth: undefined,
13053 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13054 * mode = 'remote' or 'text' if mode = 'local')
13056 displayField: undefined,
13059 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13060 * mode = 'remote' or 'value' if mode = 'local').
13061 * Note: use of a valueField requires the user make a selection
13062 * in order for a value to be mapped.
13064 valueField: undefined,
13066 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13071 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13072 * field's data value (defaults to the underlying DOM element's name)
13074 hiddenName: undefined,
13076 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13080 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13082 selectedClass: 'active',
13085 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13089 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13090 * anchor positions (defaults to 'tl-bl')
13092 listAlign: 'tl-bl?',
13094 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13098 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13099 * query specified by the allQuery config option (defaults to 'query')
13101 triggerAction: 'query',
13103 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13104 * (defaults to 4, does not apply if editable = false)
13108 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13109 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13113 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13114 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13118 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13119 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13123 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13124 * when editable = true (defaults to false)
13126 selectOnFocus:false,
13128 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13130 queryParam: 'query',
13132 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13133 * when mode = 'remote' (defaults to 'Loading...')
13135 loadingText: 'Loading...',
13137 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13141 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13145 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13146 * traditional select (defaults to true)
13150 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13154 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13158 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13159 * listWidth has a higher value)
13163 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13164 * allow the user to set arbitrary text into the field (defaults to false)
13166 forceSelection:false,
13168 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13169 * if typeAhead = true (defaults to 250)
13171 typeAheadDelay : 250,
13173 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13174 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13176 valueNotFoundText : undefined,
13178 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13180 blockFocus : false,
13183 * @cfg {Boolean} disableClear Disable showing of clear button.
13185 disableClear : false,
13187 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13189 alwaysQuery : false,
13192 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13197 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13199 invalidClass : "has-warning",
13202 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13204 validClass : "has-success",
13207 * @cfg {Boolean} specialFilter (true|false) special filter default false
13209 specialFilter : false,
13212 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13214 mobileTouchView : true,
13217 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13219 useNativeIOS : false,
13222 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13224 mobile_restrict_height : false,
13226 ios_options : false,
13238 btnPosition : 'right',
13239 triggerList : true,
13240 showToggleBtn : true,
13242 emptyResultText: 'Empty',
13243 triggerText : 'Select',
13246 // element that contains real text value.. (when hidden is used..)
13248 getAutoCreate : function()
13253 * Render classic select for iso
13256 if(Roo.isIOS && this.useNativeIOS){
13257 cfg = this.getAutoCreateNativeIOS();
13265 if(Roo.isTouch && this.mobileTouchView){
13266 cfg = this.getAutoCreateTouchView();
13273 if(!this.tickable){
13274 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13279 * ComboBox with tickable selections
13282 var align = this.labelAlign || this.parentLabelAlign();
13285 cls : 'form-group roo-combobox-tickable' //input-group
13288 var btn_text_select = '';
13289 var btn_text_done = '';
13290 var btn_text_cancel = '';
13292 if (this.btn_text_show) {
13293 btn_text_select = 'Select';
13294 btn_text_done = 'Done';
13295 btn_text_cancel = 'Cancel';
13300 cls : 'tickable-buttons',
13305 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13306 //html : this.triggerText
13307 html: btn_text_select
13313 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13315 html: btn_text_done
13321 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13323 html: btn_text_cancel
13329 buttons.cn.unshift({
13331 cls: 'roo-select2-search-field-input'
13337 Roo.each(buttons.cn, function(c){
13339 c.cls += ' btn-' + _this.size;
13342 if (_this.disabled) {
13353 cls: 'form-hidden-field'
13357 cls: 'roo-select2-choices',
13361 cls: 'roo-select2-search-field',
13372 cls: 'roo-select2-container input-group roo-select2-container-multi',
13378 // cls: 'typeahead typeahead-long dropdown-menu',
13379 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13384 if(this.hasFeedback && !this.allowBlank){
13388 cls: 'glyphicon form-control-feedback'
13391 combobox.cn.push(feedback);
13396 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13397 tooltip : 'This field is required'
13399 if (Roo.bootstrap.version == 4) {
13402 style : 'display:none'
13405 if (align ==='left' && this.fieldLabel.length) {
13407 cfg.cls += ' roo-form-group-label-left row';
13414 cls : 'control-label col-form-label',
13415 html : this.fieldLabel
13427 var labelCfg = cfg.cn[1];
13428 var contentCfg = cfg.cn[2];
13431 if(this.indicatorpos == 'right'){
13437 cls : 'control-label col-form-label',
13441 html : this.fieldLabel
13457 labelCfg = cfg.cn[0];
13458 contentCfg = cfg.cn[1];
13462 if(this.labelWidth > 12){
13463 labelCfg.style = "width: " + this.labelWidth + 'px';
13466 if(this.labelWidth < 13 && this.labelmd == 0){
13467 this.labelmd = this.labelWidth;
13470 if(this.labellg > 0){
13471 labelCfg.cls += ' col-lg-' + this.labellg;
13472 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13475 if(this.labelmd > 0){
13476 labelCfg.cls += ' col-md-' + this.labelmd;
13477 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13480 if(this.labelsm > 0){
13481 labelCfg.cls += ' col-sm-' + this.labelsm;
13482 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13485 if(this.labelxs > 0){
13486 labelCfg.cls += ' col-xs-' + this.labelxs;
13487 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13491 } else if ( this.fieldLabel.length) {
13492 // Roo.log(" label");
13497 //cls : 'input-group-addon',
13498 html : this.fieldLabel
13503 if(this.indicatorpos == 'right'){
13507 //cls : 'input-group-addon',
13508 html : this.fieldLabel
13518 // Roo.log(" no label && no align");
13525 ['xs','sm','md','lg'].map(function(size){
13526 if (settings[size]) {
13527 cfg.cls += ' col-' + size + '-' + settings[size];
13535 _initEventsCalled : false,
13538 initEvents: function()
13540 if (this._initEventsCalled) { // as we call render... prevent looping...
13543 this._initEventsCalled = true;
13546 throw "can not find store for combo";
13549 this.indicator = this.indicatorEl();
13551 this.store = Roo.factory(this.store, Roo.data);
13552 this.store.parent = this;
13554 // if we are building from html. then this element is so complex, that we can not really
13555 // use the rendered HTML.
13556 // so we have to trash and replace the previous code.
13557 if (Roo.XComponent.build_from_html) {
13558 // remove this element....
13559 var e = this.el.dom, k=0;
13560 while (e ) { e = e.previousSibling; ++k;}
13565 this.rendered = false;
13567 this.render(this.parent().getChildContainer(true), k);
13570 if(Roo.isIOS && this.useNativeIOS){
13571 this.initIOSView();
13579 if(Roo.isTouch && this.mobileTouchView){
13580 this.initTouchView();
13585 this.initTickableEvents();
13589 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13591 if(this.hiddenName){
13593 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13595 this.hiddenField.dom.value =
13596 this.hiddenValue !== undefined ? this.hiddenValue :
13597 this.value !== undefined ? this.value : '';
13599 // prevent input submission
13600 this.el.dom.removeAttribute('name');
13601 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13606 // this.el.dom.setAttribute('autocomplete', 'off');
13609 var cls = 'x-combo-list';
13611 //this.list = new Roo.Layer({
13612 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13618 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13619 _this.list.setWidth(lw);
13622 this.list.on('mouseover', this.onViewOver, this);
13623 this.list.on('mousemove', this.onViewMove, this);
13624 this.list.on('scroll', this.onViewScroll, this);
13627 this.list.swallowEvent('mousewheel');
13628 this.assetHeight = 0;
13631 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13632 this.assetHeight += this.header.getHeight();
13635 this.innerList = this.list.createChild({cls:cls+'-inner'});
13636 this.innerList.on('mouseover', this.onViewOver, this);
13637 this.innerList.on('mousemove', this.onViewMove, this);
13638 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13640 if(this.allowBlank && !this.pageSize && !this.disableClear){
13641 this.footer = this.list.createChild({cls:cls+'-ft'});
13642 this.pageTb = new Roo.Toolbar(this.footer);
13646 this.footer = this.list.createChild({cls:cls+'-ft'});
13647 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13648 {pageSize: this.pageSize});
13652 if (this.pageTb && this.allowBlank && !this.disableClear) {
13654 this.pageTb.add(new Roo.Toolbar.Fill(), {
13655 cls: 'x-btn-icon x-btn-clear',
13657 handler: function()
13660 _this.clearValue();
13661 _this.onSelect(false, -1);
13666 this.assetHeight += this.footer.getHeight();
13671 this.tpl = Roo.bootstrap.version == 4 ?
13672 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13673 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13676 this.view = new Roo.View(this.list, this.tpl, {
13677 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13679 //this.view.wrapEl.setDisplayed(false);
13680 this.view.on('click', this.onViewClick, this);
13683 this.store.on('beforeload', this.onBeforeLoad, this);
13684 this.store.on('load', this.onLoad, this);
13685 this.store.on('loadexception', this.onLoadException, this);
13687 if(this.resizable){
13688 this.resizer = new Roo.Resizable(this.list, {
13689 pinned:true, handles:'se'
13691 this.resizer.on('resize', function(r, w, h){
13692 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13693 this.listWidth = w;
13694 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13695 this.restrictHeight();
13697 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13700 if(!this.editable){
13701 this.editable = true;
13702 this.setEditable(false);
13707 if (typeof(this.events.add.listeners) != 'undefined') {
13709 this.addicon = this.wrap.createChild(
13710 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13712 this.addicon.on('click', function(e) {
13713 this.fireEvent('add', this);
13716 if (typeof(this.events.edit.listeners) != 'undefined') {
13718 this.editicon = this.wrap.createChild(
13719 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13720 if (this.addicon) {
13721 this.editicon.setStyle('margin-left', '40px');
13723 this.editicon.on('click', function(e) {
13725 // we fire even if inothing is selected..
13726 this.fireEvent('edit', this, this.lastData );
13732 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13733 "up" : function(e){
13734 this.inKeyMode = true;
13738 "down" : function(e){
13739 if(!this.isExpanded()){
13740 this.onTriggerClick();
13742 this.inKeyMode = true;
13747 "enter" : function(e){
13748 // this.onViewClick();
13752 if(this.fireEvent("specialkey", this, e)){
13753 this.onViewClick(false);
13759 "esc" : function(e){
13763 "tab" : function(e){
13766 if(this.fireEvent("specialkey", this, e)){
13767 this.onViewClick(false);
13775 doRelay : function(foo, bar, hname){
13776 if(hname == 'down' || this.scope.isExpanded()){
13777 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13786 this.queryDelay = Math.max(this.queryDelay || 10,
13787 this.mode == 'local' ? 10 : 250);
13790 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13792 if(this.typeAhead){
13793 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13795 if(this.editable !== false){
13796 this.inputEl().on("keyup", this.onKeyUp, this);
13798 if(this.forceSelection){
13799 this.inputEl().on('blur', this.doForce, this);
13803 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13804 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13808 initTickableEvents: function()
13812 if(this.hiddenName){
13814 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13816 this.hiddenField.dom.value =
13817 this.hiddenValue !== undefined ? this.hiddenValue :
13818 this.value !== undefined ? this.value : '';
13820 // prevent input submission
13821 this.el.dom.removeAttribute('name');
13822 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13827 // this.list = this.el.select('ul.dropdown-menu',true).first();
13829 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13830 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13831 if(this.triggerList){
13832 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13835 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13836 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13838 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13839 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13841 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13842 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13844 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13845 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13846 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13849 this.cancelBtn.hide();
13854 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13855 _this.list.setWidth(lw);
13858 this.list.on('mouseover', this.onViewOver, this);
13859 this.list.on('mousemove', this.onViewMove, this);
13861 this.list.on('scroll', this.onViewScroll, this);
13864 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13865 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13868 this.view = new Roo.View(this.list, this.tpl, {
13873 selectedClass: this.selectedClass
13876 //this.view.wrapEl.setDisplayed(false);
13877 this.view.on('click', this.onViewClick, this);
13881 this.store.on('beforeload', this.onBeforeLoad, this);
13882 this.store.on('load', this.onLoad, this);
13883 this.store.on('loadexception', this.onLoadException, this);
13886 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13887 "up" : function(e){
13888 this.inKeyMode = true;
13892 "down" : function(e){
13893 this.inKeyMode = true;
13897 "enter" : function(e){
13898 if(this.fireEvent("specialkey", this, e)){
13899 this.onViewClick(false);
13905 "esc" : function(e){
13906 this.onTickableFooterButtonClick(e, false, false);
13909 "tab" : function(e){
13910 this.fireEvent("specialkey", this, e);
13912 this.onTickableFooterButtonClick(e, false, false);
13919 doRelay : function(e, fn, key){
13920 if(this.scope.isExpanded()){
13921 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13930 this.queryDelay = Math.max(this.queryDelay || 10,
13931 this.mode == 'local' ? 10 : 250);
13934 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13936 if(this.typeAhead){
13937 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13940 if(this.editable !== false){
13941 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13944 this.indicator = this.indicatorEl();
13946 if(this.indicator){
13947 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13948 this.indicator.hide();
13953 onDestroy : function(){
13955 this.view.setStore(null);
13956 this.view.el.removeAllListeners();
13957 this.view.el.remove();
13958 this.view.purgeListeners();
13961 this.list.dom.innerHTML = '';
13965 this.store.un('beforeload', this.onBeforeLoad, this);
13966 this.store.un('load', this.onLoad, this);
13967 this.store.un('loadexception', this.onLoadException, this);
13969 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13973 fireKey : function(e){
13974 if(e.isNavKeyPress() && !this.list.isVisible()){
13975 this.fireEvent("specialkey", this, e);
13980 onResize: function(w, h){
13981 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13983 // if(typeof w != 'number'){
13984 // // we do not handle it!?!?
13987 // var tw = this.trigger.getWidth();
13988 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13989 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13991 // this.inputEl().setWidth( this.adjustWidth('input', x));
13993 // //this.trigger.setStyle('left', x+'px');
13995 // if(this.list && this.listWidth === undefined){
13996 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13997 // this.list.setWidth(lw);
13998 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14006 * Allow or prevent the user from directly editing the field text. If false is passed,
14007 * the user will only be able to select from the items defined in the dropdown list. This method
14008 * is the runtime equivalent of setting the 'editable' config option at config time.
14009 * @param {Boolean} value True to allow the user to directly edit the field text
14011 setEditable : function(value){
14012 if(value == this.editable){
14015 this.editable = value;
14017 this.inputEl().dom.setAttribute('readOnly', true);
14018 this.inputEl().on('mousedown', this.onTriggerClick, this);
14019 this.inputEl().addClass('x-combo-noedit');
14021 this.inputEl().dom.setAttribute('readOnly', false);
14022 this.inputEl().un('mousedown', this.onTriggerClick, this);
14023 this.inputEl().removeClass('x-combo-noedit');
14029 onBeforeLoad : function(combo,opts){
14030 if(!this.hasFocus){
14034 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14036 this.restrictHeight();
14037 this.selectedIndex = -1;
14041 onLoad : function(){
14043 this.hasQuery = false;
14045 if(!this.hasFocus){
14049 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14050 this.loading.hide();
14053 if(this.store.getCount() > 0){
14056 this.restrictHeight();
14057 if(this.lastQuery == this.allQuery){
14058 if(this.editable && !this.tickable){
14059 this.inputEl().dom.select();
14063 !this.selectByValue(this.value, true) &&
14066 !this.store.lastOptions ||
14067 typeof(this.store.lastOptions.add) == 'undefined' ||
14068 this.store.lastOptions.add != true
14071 this.select(0, true);
14074 if(this.autoFocus){
14077 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14078 this.taTask.delay(this.typeAheadDelay);
14082 this.onEmptyResults();
14088 onLoadException : function()
14090 this.hasQuery = false;
14092 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14093 this.loading.hide();
14096 if(this.tickable && this.editable){
14101 // only causes errors at present
14102 //Roo.log(this.store.reader.jsonData);
14103 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14105 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14111 onTypeAhead : function(){
14112 if(this.store.getCount() > 0){
14113 var r = this.store.getAt(0);
14114 var newValue = r.data[this.displayField];
14115 var len = newValue.length;
14116 var selStart = this.getRawValue().length;
14118 if(selStart != len){
14119 this.setRawValue(newValue);
14120 this.selectText(selStart, newValue.length);
14126 onSelect : function(record, index){
14128 if(this.fireEvent('beforeselect', this, record, index) !== false){
14130 this.setFromData(index > -1 ? record.data : false);
14133 this.fireEvent('select', this, record, index);
14138 * Returns the currently selected field value or empty string if no value is set.
14139 * @return {String} value The selected value
14141 getValue : function()
14143 if(Roo.isIOS && this.useNativeIOS){
14144 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14148 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14151 if(this.valueField){
14152 return typeof this.value != 'undefined' ? this.value : '';
14154 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14158 getRawValue : function()
14160 if(Roo.isIOS && this.useNativeIOS){
14161 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14164 var v = this.inputEl().getValue();
14170 * Clears any text/value currently set in the field
14172 clearValue : function(){
14174 if(this.hiddenField){
14175 this.hiddenField.dom.value = '';
14178 this.setRawValue('');
14179 this.lastSelectionText = '';
14180 this.lastData = false;
14182 var close = this.closeTriggerEl();
14193 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14194 * will be displayed in the field. If the value does not match the data value of an existing item,
14195 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14196 * Otherwise the field will be blank (although the value will still be set).
14197 * @param {String} value The value to match
14199 setValue : function(v)
14201 if(Roo.isIOS && this.useNativeIOS){
14202 this.setIOSValue(v);
14212 if(this.valueField){
14213 var r = this.findRecord(this.valueField, v);
14215 text = r.data[this.displayField];
14216 }else if(this.valueNotFoundText !== undefined){
14217 text = this.valueNotFoundText;
14220 this.lastSelectionText = text;
14221 if(this.hiddenField){
14222 this.hiddenField.dom.value = v;
14224 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14227 var close = this.closeTriggerEl();
14230 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14236 * @property {Object} the last set data for the element
14241 * Sets the value of the field based on a object which is related to the record format for the store.
14242 * @param {Object} value the value to set as. or false on reset?
14244 setFromData : function(o){
14251 var dv = ''; // display value
14252 var vv = ''; // value value..
14254 if (this.displayField) {
14255 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14257 // this is an error condition!!!
14258 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14261 if(this.valueField){
14262 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14265 var close = this.closeTriggerEl();
14268 if(dv.length || vv * 1 > 0){
14270 this.blockFocus=true;
14276 if(this.hiddenField){
14277 this.hiddenField.dom.value = vv;
14279 this.lastSelectionText = dv;
14280 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14284 // no hidden field.. - we store the value in 'value', but still display
14285 // display field!!!!
14286 this.lastSelectionText = dv;
14287 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14294 reset : function(){
14295 // overridden so that last data is reset..
14302 this.setValue(this.originalValue);
14303 //this.clearInvalid();
14304 this.lastData = false;
14306 this.view.clearSelections();
14312 findRecord : function(prop, value){
14314 if(this.store.getCount() > 0){
14315 this.store.each(function(r){
14316 if(r.data[prop] == value){
14326 getName: function()
14328 // returns hidden if it's set..
14329 if (!this.rendered) {return ''};
14330 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14334 onViewMove : function(e, t){
14335 this.inKeyMode = false;
14339 onViewOver : function(e, t){
14340 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14343 var item = this.view.findItemFromChild(t);
14346 var index = this.view.indexOf(item);
14347 this.select(index, false);
14352 onViewClick : function(view, doFocus, el, e)
14354 var index = this.view.getSelectedIndexes()[0];
14356 var r = this.store.getAt(index);
14360 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14367 Roo.each(this.tickItems, function(v,k){
14369 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14371 _this.tickItems.splice(k, 1);
14373 if(typeof(e) == 'undefined' && view == false){
14374 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14386 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14387 this.tickItems.push(r.data);
14390 if(typeof(e) == 'undefined' && view == false){
14391 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14398 this.onSelect(r, index);
14400 if(doFocus !== false && !this.blockFocus){
14401 this.inputEl().focus();
14406 restrictHeight : function(){
14407 //this.innerList.dom.style.height = '';
14408 //var inner = this.innerList.dom;
14409 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14410 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14411 //this.list.beginUpdate();
14412 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14413 this.list.alignTo(this.inputEl(), this.listAlign);
14414 this.list.alignTo(this.inputEl(), this.listAlign);
14415 //this.list.endUpdate();
14419 onEmptyResults : function(){
14421 if(this.tickable && this.editable){
14422 this.hasFocus = false;
14423 this.restrictHeight();
14431 * Returns true if the dropdown list is expanded, else false.
14433 isExpanded : function(){
14434 return this.list.isVisible();
14438 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14439 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14440 * @param {String} value The data value of the item to select
14441 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14442 * selected item if it is not currently in view (defaults to true)
14443 * @return {Boolean} True if the value matched an item in the list, else false
14445 selectByValue : function(v, scrollIntoView){
14446 if(v !== undefined && v !== null){
14447 var r = this.findRecord(this.valueField || this.displayField, v);
14449 this.select(this.store.indexOf(r), scrollIntoView);
14457 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14458 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14459 * @param {Number} index The zero-based index of the list item to select
14460 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14461 * selected item if it is not currently in view (defaults to true)
14463 select : function(index, scrollIntoView){
14464 this.selectedIndex = index;
14465 this.view.select(index);
14466 if(scrollIntoView !== false){
14467 var el = this.view.getNode(index);
14469 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14472 this.list.scrollChildIntoView(el, false);
14478 selectNext : function(){
14479 var ct = this.store.getCount();
14481 if(this.selectedIndex == -1){
14483 }else if(this.selectedIndex < ct-1){
14484 this.select(this.selectedIndex+1);
14490 selectPrev : function(){
14491 var ct = this.store.getCount();
14493 if(this.selectedIndex == -1){
14495 }else if(this.selectedIndex != 0){
14496 this.select(this.selectedIndex-1);
14502 onKeyUp : function(e){
14503 if(this.editable !== false && !e.isSpecialKey()){
14504 this.lastKey = e.getKey();
14505 this.dqTask.delay(this.queryDelay);
14510 validateBlur : function(){
14511 return !this.list || !this.list.isVisible();
14515 initQuery : function(){
14517 var v = this.getRawValue();
14519 if(this.tickable && this.editable){
14520 v = this.tickableInputEl().getValue();
14527 doForce : function(){
14528 if(this.inputEl().dom.value.length > 0){
14529 this.inputEl().dom.value =
14530 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14536 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14537 * query allowing the query action to be canceled if needed.
14538 * @param {String} query The SQL query to execute
14539 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14540 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14541 * saved in the current store (defaults to false)
14543 doQuery : function(q, forceAll){
14545 if(q === undefined || q === null){
14550 forceAll: forceAll,
14554 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14559 forceAll = qe.forceAll;
14560 if(forceAll === true || (q.length >= this.minChars)){
14562 this.hasQuery = true;
14564 if(this.lastQuery != q || this.alwaysQuery){
14565 this.lastQuery = q;
14566 if(this.mode == 'local'){
14567 this.selectedIndex = -1;
14569 this.store.clearFilter();
14572 if(this.specialFilter){
14573 this.fireEvent('specialfilter', this);
14578 this.store.filter(this.displayField, q);
14581 this.store.fireEvent("datachanged", this.store);
14588 this.store.baseParams[this.queryParam] = q;
14590 var options = {params : this.getParams(q)};
14593 options.add = true;
14594 options.params.start = this.page * this.pageSize;
14597 this.store.load(options);
14600 * this code will make the page width larger, at the beginning, the list not align correctly,
14601 * we should expand the list on onLoad
14602 * so command out it
14607 this.selectedIndex = -1;
14612 this.loadNext = false;
14616 getParams : function(q){
14618 //p[this.queryParam] = q;
14622 p.limit = this.pageSize;
14628 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14630 collapse : function(){
14631 if(!this.isExpanded()){
14637 this.hasFocus = false;
14641 this.cancelBtn.hide();
14642 this.trigger.show();
14645 this.tickableInputEl().dom.value = '';
14646 this.tickableInputEl().blur();
14651 Roo.get(document).un('mousedown', this.collapseIf, this);
14652 Roo.get(document).un('mousewheel', this.collapseIf, this);
14653 if (!this.editable) {
14654 Roo.get(document).un('keydown', this.listKeyPress, this);
14656 this.fireEvent('collapse', this);
14662 collapseIf : function(e){
14663 var in_combo = e.within(this.el);
14664 var in_list = e.within(this.list);
14665 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14667 if (in_combo || in_list || is_list) {
14668 //e.stopPropagation();
14673 this.onTickableFooterButtonClick(e, false, false);
14681 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14683 expand : function(){
14685 if(this.isExpanded() || !this.hasFocus){
14689 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14690 this.list.setWidth(lw);
14696 this.restrictHeight();
14700 this.tickItems = Roo.apply([], this.item);
14703 this.cancelBtn.show();
14704 this.trigger.hide();
14707 this.tickableInputEl().focus();
14712 Roo.get(document).on('mousedown', this.collapseIf, this);
14713 Roo.get(document).on('mousewheel', this.collapseIf, this);
14714 if (!this.editable) {
14715 Roo.get(document).on('keydown', this.listKeyPress, this);
14718 this.fireEvent('expand', this);
14722 // Implements the default empty TriggerField.onTriggerClick function
14723 onTriggerClick : function(e)
14725 Roo.log('trigger click');
14727 if(this.disabled || !this.triggerList){
14732 this.loadNext = false;
14734 if(this.isExpanded()){
14736 if (!this.blockFocus) {
14737 this.inputEl().focus();
14741 this.hasFocus = true;
14742 if(this.triggerAction == 'all') {
14743 this.doQuery(this.allQuery, true);
14745 this.doQuery(this.getRawValue());
14747 if (!this.blockFocus) {
14748 this.inputEl().focus();
14753 onTickableTriggerClick : function(e)
14760 this.loadNext = false;
14761 this.hasFocus = true;
14763 if(this.triggerAction == 'all') {
14764 this.doQuery(this.allQuery, true);
14766 this.doQuery(this.getRawValue());
14770 onSearchFieldClick : function(e)
14772 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14773 this.onTickableFooterButtonClick(e, false, false);
14777 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14782 this.loadNext = false;
14783 this.hasFocus = true;
14785 if(this.triggerAction == 'all') {
14786 this.doQuery(this.allQuery, true);
14788 this.doQuery(this.getRawValue());
14792 listKeyPress : function(e)
14794 //Roo.log('listkeypress');
14795 // scroll to first matching element based on key pres..
14796 if (e.isSpecialKey()) {
14799 var k = String.fromCharCode(e.getKey()).toUpperCase();
14802 var csel = this.view.getSelectedNodes();
14803 var cselitem = false;
14805 var ix = this.view.indexOf(csel[0]);
14806 cselitem = this.store.getAt(ix);
14807 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14813 this.store.each(function(v) {
14815 // start at existing selection.
14816 if (cselitem.id == v.id) {
14822 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14823 match = this.store.indexOf(v);
14829 if (match === false) {
14830 return true; // no more action?
14833 this.view.select(match);
14834 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14835 sn.scrollIntoView(sn.dom.parentNode, false);
14838 onViewScroll : function(e, t){
14840 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){
14844 this.hasQuery = true;
14846 this.loading = this.list.select('.loading', true).first();
14848 if(this.loading === null){
14849 this.list.createChild({
14851 cls: 'loading roo-select2-more-results roo-select2-active',
14852 html: 'Loading more results...'
14855 this.loading = this.list.select('.loading', true).first();
14857 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14859 this.loading.hide();
14862 this.loading.show();
14867 this.loadNext = true;
14869 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14874 addItem : function(o)
14876 var dv = ''; // display value
14878 if (this.displayField) {
14879 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14881 // this is an error condition!!!
14882 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14889 var choice = this.choices.createChild({
14891 cls: 'roo-select2-search-choice',
14900 cls: 'roo-select2-search-choice-close fa fa-times',
14905 }, this.searchField);
14907 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14909 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14917 this.inputEl().dom.value = '';
14922 onRemoveItem : function(e, _self, o)
14924 e.preventDefault();
14926 this.lastItem = Roo.apply([], this.item);
14928 var index = this.item.indexOf(o.data) * 1;
14931 Roo.log('not this item?!');
14935 this.item.splice(index, 1);
14940 this.fireEvent('remove', this, e);
14946 syncValue : function()
14948 if(!this.item.length){
14955 Roo.each(this.item, function(i){
14956 if(_this.valueField){
14957 value.push(i[_this.valueField]);
14964 this.value = value.join(',');
14966 if(this.hiddenField){
14967 this.hiddenField.dom.value = this.value;
14970 this.store.fireEvent("datachanged", this.store);
14975 clearItem : function()
14977 if(!this.multiple){
14983 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14991 if(this.tickable && !Roo.isTouch){
14992 this.view.refresh();
14996 inputEl: function ()
14998 if(Roo.isIOS && this.useNativeIOS){
14999 return this.el.select('select.roo-ios-select', true).first();
15002 if(Roo.isTouch && this.mobileTouchView){
15003 return this.el.select('input.form-control',true).first();
15007 return this.searchField;
15010 return this.el.select('input.form-control',true).first();
15013 onTickableFooterButtonClick : function(e, btn, el)
15015 e.preventDefault();
15017 this.lastItem = Roo.apply([], this.item);
15019 if(btn && btn.name == 'cancel'){
15020 this.tickItems = Roo.apply([], this.item);
15029 Roo.each(this.tickItems, function(o){
15037 validate : function()
15039 if(this.getVisibilityEl().hasClass('hidden')){
15043 var v = this.getRawValue();
15046 v = this.getValue();
15049 if(this.disabled || this.allowBlank || v.length){
15054 this.markInvalid();
15058 tickableInputEl : function()
15060 if(!this.tickable || !this.editable){
15061 return this.inputEl();
15064 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15068 getAutoCreateTouchView : function()
15073 cls: 'form-group' //input-group
15079 type : this.inputType,
15080 cls : 'form-control x-combo-noedit',
15081 autocomplete: 'new-password',
15082 placeholder : this.placeholder || '',
15087 input.name = this.name;
15091 input.cls += ' input-' + this.size;
15094 if (this.disabled) {
15095 input.disabled = true;
15106 inputblock.cls += ' input-group';
15108 inputblock.cn.unshift({
15110 cls : 'input-group-addon input-group-prepend input-group-text',
15115 if(this.removable && !this.multiple){
15116 inputblock.cls += ' roo-removable';
15118 inputblock.cn.push({
15121 cls : 'roo-combo-removable-btn close'
15125 if(this.hasFeedback && !this.allowBlank){
15127 inputblock.cls += ' has-feedback';
15129 inputblock.cn.push({
15131 cls: 'glyphicon form-control-feedback'
15138 inputblock.cls += (this.before) ? '' : ' input-group';
15140 inputblock.cn.push({
15142 cls : 'input-group-addon input-group-append input-group-text',
15148 var ibwrap = inputblock;
15153 cls: 'roo-select2-choices',
15157 cls: 'roo-select2-search-field',
15170 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15175 cls: 'form-hidden-field'
15181 if(!this.multiple && this.showToggleBtn){
15188 if (this.caret != false) {
15191 cls: 'fa fa-' + this.caret
15198 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15203 cls: 'combobox-clear',
15217 combobox.cls += ' roo-select2-container-multi';
15220 var align = this.labelAlign || this.parentLabelAlign();
15222 if (align ==='left' && this.fieldLabel.length) {
15227 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15228 tooltip : 'This field is required'
15232 cls : 'control-label col-form-label',
15233 html : this.fieldLabel
15244 var labelCfg = cfg.cn[1];
15245 var contentCfg = cfg.cn[2];
15248 if(this.indicatorpos == 'right'){
15253 cls : 'control-label col-form-label',
15257 html : this.fieldLabel
15261 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15262 tooltip : 'This field is required'
15275 labelCfg = cfg.cn[0];
15276 contentCfg = cfg.cn[1];
15281 if(this.labelWidth > 12){
15282 labelCfg.style = "width: " + this.labelWidth + 'px';
15285 if(this.labelWidth < 13 && this.labelmd == 0){
15286 this.labelmd = this.labelWidth;
15289 if(this.labellg > 0){
15290 labelCfg.cls += ' col-lg-' + this.labellg;
15291 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15294 if(this.labelmd > 0){
15295 labelCfg.cls += ' col-md-' + this.labelmd;
15296 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15299 if(this.labelsm > 0){
15300 labelCfg.cls += ' col-sm-' + this.labelsm;
15301 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15304 if(this.labelxs > 0){
15305 labelCfg.cls += ' col-xs-' + this.labelxs;
15306 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15310 } else if ( this.fieldLabel.length) {
15314 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15315 tooltip : 'This field is required'
15319 cls : 'control-label',
15320 html : this.fieldLabel
15331 if(this.indicatorpos == 'right'){
15335 cls : 'control-label',
15336 html : this.fieldLabel,
15340 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15341 tooltip : 'This field is required'
15358 var settings = this;
15360 ['xs','sm','md','lg'].map(function(size){
15361 if (settings[size]) {
15362 cfg.cls += ' col-' + size + '-' + settings[size];
15369 initTouchView : function()
15371 this.renderTouchView();
15373 this.touchViewEl.on('scroll', function(){
15374 this.el.dom.scrollTop = 0;
15377 this.originalValue = this.getValue();
15379 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15381 this.inputEl().on("click", this.showTouchView, this);
15382 if (this.triggerEl) {
15383 this.triggerEl.on("click", this.showTouchView, this);
15387 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15388 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15390 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15392 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15393 this.store.on('load', this.onTouchViewLoad, this);
15394 this.store.on('loadexception', this.onTouchViewLoadException, this);
15396 if(this.hiddenName){
15398 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15400 this.hiddenField.dom.value =
15401 this.hiddenValue !== undefined ? this.hiddenValue :
15402 this.value !== undefined ? this.value : '';
15404 this.el.dom.removeAttribute('name');
15405 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15409 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15410 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15413 if(this.removable && !this.multiple){
15414 var close = this.closeTriggerEl();
15416 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15417 close.on('click', this.removeBtnClick, this, close);
15421 * fix the bug in Safari iOS8
15423 this.inputEl().on("focus", function(e){
15424 document.activeElement.blur();
15427 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15434 renderTouchView : function()
15436 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15437 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15439 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15440 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15442 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15443 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15444 this.touchViewBodyEl.setStyle('overflow', 'auto');
15446 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15447 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15449 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15450 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15454 showTouchView : function()
15460 this.touchViewHeaderEl.hide();
15462 if(this.modalTitle.length){
15463 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15464 this.touchViewHeaderEl.show();
15467 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15468 this.touchViewEl.show();
15470 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15472 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15473 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15475 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15477 if(this.modalTitle.length){
15478 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15481 this.touchViewBodyEl.setHeight(bodyHeight);
15485 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15487 this.touchViewEl.addClass('in');
15490 if(this._touchViewMask){
15491 Roo.get(document.body).addClass("x-body-masked");
15492 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15493 this._touchViewMask.setStyle('z-index', 10000);
15494 this._touchViewMask.addClass('show');
15497 this.doTouchViewQuery();
15501 hideTouchView : function()
15503 this.touchViewEl.removeClass('in');
15507 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15509 this.touchViewEl.setStyle('display', 'none');
15512 if(this._touchViewMask){
15513 this._touchViewMask.removeClass('show');
15514 Roo.get(document.body).removeClass("x-body-masked");
15518 setTouchViewValue : function()
15525 Roo.each(this.tickItems, function(o){
15530 this.hideTouchView();
15533 doTouchViewQuery : function()
15542 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15546 if(!this.alwaysQuery || this.mode == 'local'){
15547 this.onTouchViewLoad();
15554 onTouchViewBeforeLoad : function(combo,opts)
15560 onTouchViewLoad : function()
15562 if(this.store.getCount() < 1){
15563 this.onTouchViewEmptyResults();
15567 this.clearTouchView();
15569 var rawValue = this.getRawValue();
15571 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15573 this.tickItems = [];
15575 this.store.data.each(function(d, rowIndex){
15576 var row = this.touchViewListGroup.createChild(template);
15578 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15579 row.addClass(d.data.cls);
15582 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15585 html : d.data[this.displayField]
15588 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15589 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15592 row.removeClass('selected');
15593 if(!this.multiple && this.valueField &&
15594 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15597 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15598 row.addClass('selected');
15601 if(this.multiple && this.valueField &&
15602 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15606 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15607 this.tickItems.push(d.data);
15610 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15614 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15616 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15618 if(this.modalTitle.length){
15619 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15622 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15624 if(this.mobile_restrict_height && listHeight < bodyHeight){
15625 this.touchViewBodyEl.setHeight(listHeight);
15630 if(firstChecked && listHeight > bodyHeight){
15631 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15636 onTouchViewLoadException : function()
15638 this.hideTouchView();
15641 onTouchViewEmptyResults : function()
15643 this.clearTouchView();
15645 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15647 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15651 clearTouchView : function()
15653 this.touchViewListGroup.dom.innerHTML = '';
15656 onTouchViewClick : function(e, el, o)
15658 e.preventDefault();
15661 var rowIndex = o.rowIndex;
15663 var r = this.store.getAt(rowIndex);
15665 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15667 if(!this.multiple){
15668 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15669 c.dom.removeAttribute('checked');
15672 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15674 this.setFromData(r.data);
15676 var close = this.closeTriggerEl();
15682 this.hideTouchView();
15684 this.fireEvent('select', this, r, rowIndex);
15689 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15690 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15691 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15695 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15696 this.addItem(r.data);
15697 this.tickItems.push(r.data);
15701 getAutoCreateNativeIOS : function()
15704 cls: 'form-group' //input-group,
15709 cls : 'roo-ios-select'
15713 combobox.name = this.name;
15716 if (this.disabled) {
15717 combobox.disabled = true;
15720 var settings = this;
15722 ['xs','sm','md','lg'].map(function(size){
15723 if (settings[size]) {
15724 cfg.cls += ' col-' + size + '-' + settings[size];
15734 initIOSView : function()
15736 this.store.on('load', this.onIOSViewLoad, this);
15741 onIOSViewLoad : function()
15743 if(this.store.getCount() < 1){
15747 this.clearIOSView();
15749 if(this.allowBlank) {
15751 var default_text = '-- SELECT --';
15753 if(this.placeholder.length){
15754 default_text = this.placeholder;
15757 if(this.emptyTitle.length){
15758 default_text += ' - ' + this.emptyTitle + ' -';
15761 var opt = this.inputEl().createChild({
15764 html : default_text
15768 o[this.valueField] = 0;
15769 o[this.displayField] = default_text;
15771 this.ios_options.push({
15778 this.store.data.each(function(d, rowIndex){
15782 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15783 html = d.data[this.displayField];
15788 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15789 value = d.data[this.valueField];
15798 if(this.value == d.data[this.valueField]){
15799 option['selected'] = true;
15802 var opt = this.inputEl().createChild(option);
15804 this.ios_options.push({
15811 this.inputEl().on('change', function(){
15812 this.fireEvent('select', this);
15817 clearIOSView: function()
15819 this.inputEl().dom.innerHTML = '';
15821 this.ios_options = [];
15824 setIOSValue: function(v)
15828 if(!this.ios_options){
15832 Roo.each(this.ios_options, function(opts){
15834 opts.el.dom.removeAttribute('selected');
15836 if(opts.data[this.valueField] != v){
15840 opts.el.dom.setAttribute('selected', true);
15846 * @cfg {Boolean} grow
15850 * @cfg {Number} growMin
15854 * @cfg {Number} growMax
15863 Roo.apply(Roo.bootstrap.ComboBox, {
15867 cls: 'modal-header',
15889 cls: 'list-group-item',
15893 cls: 'roo-combobox-list-group-item-value'
15897 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15911 listItemCheckbox : {
15913 cls: 'list-group-item',
15917 cls: 'roo-combobox-list-group-item-value'
15921 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15937 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15942 cls: 'modal-footer',
15950 cls: 'col-xs-6 text-left',
15953 cls: 'btn btn-danger roo-touch-view-cancel',
15959 cls: 'col-xs-6 text-right',
15962 cls: 'btn btn-success roo-touch-view-ok',
15973 Roo.apply(Roo.bootstrap.ComboBox, {
15975 touchViewTemplate : {
15977 cls: 'modal fade roo-combobox-touch-view',
15981 cls: 'modal-dialog',
15982 style : 'position:fixed', // we have to fix position....
15986 cls: 'modal-content',
15988 Roo.bootstrap.ComboBox.header,
15989 Roo.bootstrap.ComboBox.body,
15990 Roo.bootstrap.ComboBox.footer
15999 * Ext JS Library 1.1.1
16000 * Copyright(c) 2006-2007, Ext JS, LLC.
16002 * Originally Released Under LGPL - original licence link has changed is not relivant.
16005 * <script type="text/javascript">
16010 * @extends Roo.util.Observable
16011 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16012 * This class also supports single and multi selection modes. <br>
16013 * Create a data model bound view:
16015 var store = new Roo.data.Store(...);
16017 var view = new Roo.View({
16019 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16021 singleSelect: true,
16022 selectedClass: "ydataview-selected",
16026 // listen for node click?
16027 view.on("click", function(vw, index, node, e){
16028 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16032 dataModel.load("foobar.xml");
16034 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16036 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16037 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16039 * Note: old style constructor is still suported (container, template, config)
16042 * Create a new View
16043 * @param {Object} config The config object
16046 Roo.View = function(config, depreciated_tpl, depreciated_config){
16048 this.parent = false;
16050 if (typeof(depreciated_tpl) == 'undefined') {
16051 // new way.. - universal constructor.
16052 Roo.apply(this, config);
16053 this.el = Roo.get(this.el);
16056 this.el = Roo.get(config);
16057 this.tpl = depreciated_tpl;
16058 Roo.apply(this, depreciated_config);
16060 this.wrapEl = this.el.wrap().wrap();
16061 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16064 if(typeof(this.tpl) == "string"){
16065 this.tpl = new Roo.Template(this.tpl);
16067 // support xtype ctors..
16068 this.tpl = new Roo.factory(this.tpl, Roo);
16072 this.tpl.compile();
16077 * @event beforeclick
16078 * Fires before a click is processed. Returns false to cancel the default action.
16079 * @param {Roo.View} this
16080 * @param {Number} index The index of the target node
16081 * @param {HTMLElement} node The target node
16082 * @param {Roo.EventObject} e The raw event object
16084 "beforeclick" : true,
16087 * Fires when a template node is clicked.
16088 * @param {Roo.View} this
16089 * @param {Number} index The index of the target node
16090 * @param {HTMLElement} node The target node
16091 * @param {Roo.EventObject} e The raw event object
16096 * Fires when a template node is double clicked.
16097 * @param {Roo.View} this
16098 * @param {Number} index The index of the target node
16099 * @param {HTMLElement} node The target node
16100 * @param {Roo.EventObject} e The raw event object
16104 * @event contextmenu
16105 * Fires when a template node is right clicked.
16106 * @param {Roo.View} this
16107 * @param {Number} index The index of the target node
16108 * @param {HTMLElement} node The target node
16109 * @param {Roo.EventObject} e The raw event object
16111 "contextmenu" : true,
16113 * @event selectionchange
16114 * Fires when the selected nodes change.
16115 * @param {Roo.View} this
16116 * @param {Array} selections Array of the selected nodes
16118 "selectionchange" : true,
16121 * @event beforeselect
16122 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16123 * @param {Roo.View} this
16124 * @param {HTMLElement} node The node to be selected
16125 * @param {Array} selections Array of currently selected nodes
16127 "beforeselect" : true,
16129 * @event preparedata
16130 * Fires on every row to render, to allow you to change the data.
16131 * @param {Roo.View} this
16132 * @param {Object} data to be rendered (change this)
16134 "preparedata" : true
16142 "click": this.onClick,
16143 "dblclick": this.onDblClick,
16144 "contextmenu": this.onContextMenu,
16148 this.selections = [];
16150 this.cmp = new Roo.CompositeElementLite([]);
16152 this.store = Roo.factory(this.store, Roo.data);
16153 this.setStore(this.store, true);
16156 if ( this.footer && this.footer.xtype) {
16158 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16160 this.footer.dataSource = this.store;
16161 this.footer.container = fctr;
16162 this.footer = Roo.factory(this.footer, Roo);
16163 fctr.insertFirst(this.el);
16165 // this is a bit insane - as the paging toolbar seems to detach the el..
16166 // dom.parentNode.parentNode.parentNode
16167 // they get detached?
16171 Roo.View.superclass.constructor.call(this);
16176 Roo.extend(Roo.View, Roo.util.Observable, {
16179 * @cfg {Roo.data.Store} store Data store to load data from.
16184 * @cfg {String|Roo.Element} el The container element.
16189 * @cfg {String|Roo.Template} tpl The template used by this View
16193 * @cfg {String} dataName the named area of the template to use as the data area
16194 * Works with domtemplates roo-name="name"
16198 * @cfg {String} selectedClass The css class to add to selected nodes
16200 selectedClass : "x-view-selected",
16202 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16207 * @cfg {String} text to display on mask (default Loading)
16211 * @cfg {Boolean} multiSelect Allow multiple selection
16213 multiSelect : false,
16215 * @cfg {Boolean} singleSelect Allow single selection
16217 singleSelect: false,
16220 * @cfg {Boolean} toggleSelect - selecting
16222 toggleSelect : false,
16225 * @cfg {Boolean} tickable - selecting
16230 * Returns the element this view is bound to.
16231 * @return {Roo.Element}
16233 getEl : function(){
16234 return this.wrapEl;
16240 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16242 refresh : function(){
16243 //Roo.log('refresh');
16246 // if we are using something like 'domtemplate', then
16247 // the what gets used is:
16248 // t.applySubtemplate(NAME, data, wrapping data..)
16249 // the outer template then get' applied with
16250 // the store 'extra data'
16251 // and the body get's added to the
16252 // roo-name="data" node?
16253 // <span class='roo-tpl-{name}'></span> ?????
16257 this.clearSelections();
16258 this.el.update("");
16260 var records = this.store.getRange();
16261 if(records.length < 1) {
16263 // is this valid?? = should it render a template??
16265 this.el.update(this.emptyText);
16269 if (this.dataName) {
16270 this.el.update(t.apply(this.store.meta)); //????
16271 el = this.el.child('.roo-tpl-' + this.dataName);
16274 for(var i = 0, len = records.length; i < len; i++){
16275 var data = this.prepareData(records[i].data, i, records[i]);
16276 this.fireEvent("preparedata", this, data, i, records[i]);
16278 var d = Roo.apply({}, data);
16281 Roo.apply(d, {'roo-id' : Roo.id()});
16285 Roo.each(this.parent.item, function(item){
16286 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16289 Roo.apply(d, {'roo-data-checked' : 'checked'});
16293 html[html.length] = Roo.util.Format.trim(
16295 t.applySubtemplate(this.dataName, d, this.store.meta) :
16302 el.update(html.join(""));
16303 this.nodes = el.dom.childNodes;
16304 this.updateIndexes(0);
16309 * Function to override to reformat the data that is sent to
16310 * the template for each node.
16311 * DEPRICATED - use the preparedata event handler.
16312 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16313 * a JSON object for an UpdateManager bound view).
16315 prepareData : function(data, index, record)
16317 this.fireEvent("preparedata", this, data, index, record);
16321 onUpdate : function(ds, record){
16322 // Roo.log('on update');
16323 this.clearSelections();
16324 var index = this.store.indexOf(record);
16325 var n = this.nodes[index];
16326 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16327 n.parentNode.removeChild(n);
16328 this.updateIndexes(index, index);
16334 onAdd : function(ds, records, index)
16336 //Roo.log(['on Add', ds, records, index] );
16337 this.clearSelections();
16338 if(this.nodes.length == 0){
16342 var n = this.nodes[index];
16343 for(var i = 0, len = records.length; i < len; i++){
16344 var d = this.prepareData(records[i].data, i, records[i]);
16346 this.tpl.insertBefore(n, d);
16349 this.tpl.append(this.el, d);
16352 this.updateIndexes(index);
16355 onRemove : function(ds, record, index){
16356 // Roo.log('onRemove');
16357 this.clearSelections();
16358 var el = this.dataName ?
16359 this.el.child('.roo-tpl-' + this.dataName) :
16362 el.dom.removeChild(this.nodes[index]);
16363 this.updateIndexes(index);
16367 * Refresh an individual node.
16368 * @param {Number} index
16370 refreshNode : function(index){
16371 this.onUpdate(this.store, this.store.getAt(index));
16374 updateIndexes : function(startIndex, endIndex){
16375 var ns = this.nodes;
16376 startIndex = startIndex || 0;
16377 endIndex = endIndex || ns.length - 1;
16378 for(var i = startIndex; i <= endIndex; i++){
16379 ns[i].nodeIndex = i;
16384 * Changes the data store this view uses and refresh the view.
16385 * @param {Store} store
16387 setStore : function(store, initial){
16388 if(!initial && this.store){
16389 this.store.un("datachanged", this.refresh);
16390 this.store.un("add", this.onAdd);
16391 this.store.un("remove", this.onRemove);
16392 this.store.un("update", this.onUpdate);
16393 this.store.un("clear", this.refresh);
16394 this.store.un("beforeload", this.onBeforeLoad);
16395 this.store.un("load", this.onLoad);
16396 this.store.un("loadexception", this.onLoad);
16400 store.on("datachanged", this.refresh, this);
16401 store.on("add", this.onAdd, this);
16402 store.on("remove", this.onRemove, this);
16403 store.on("update", this.onUpdate, this);
16404 store.on("clear", this.refresh, this);
16405 store.on("beforeload", this.onBeforeLoad, this);
16406 store.on("load", this.onLoad, this);
16407 store.on("loadexception", this.onLoad, this);
16415 * onbeforeLoad - masks the loading area.
16418 onBeforeLoad : function(store,opts)
16420 //Roo.log('onBeforeLoad');
16422 this.el.update("");
16424 this.el.mask(this.mask ? this.mask : "Loading" );
16426 onLoad : function ()
16433 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16434 * @param {HTMLElement} node
16435 * @return {HTMLElement} The template node
16437 findItemFromChild : function(node){
16438 var el = this.dataName ?
16439 this.el.child('.roo-tpl-' + this.dataName,true) :
16442 if(!node || node.parentNode == el){
16445 var p = node.parentNode;
16446 while(p && p != el){
16447 if(p.parentNode == el){
16456 onClick : function(e){
16457 var item = this.findItemFromChild(e.getTarget());
16459 var index = this.indexOf(item);
16460 if(this.onItemClick(item, index, e) !== false){
16461 this.fireEvent("click", this, index, item, e);
16464 this.clearSelections();
16469 onContextMenu : function(e){
16470 var item = this.findItemFromChild(e.getTarget());
16472 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16477 onDblClick : function(e){
16478 var item = this.findItemFromChild(e.getTarget());
16480 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16484 onItemClick : function(item, index, e)
16486 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16489 if (this.toggleSelect) {
16490 var m = this.isSelected(item) ? 'unselect' : 'select';
16493 _t[m](item, true, false);
16496 if(this.multiSelect || this.singleSelect){
16497 if(this.multiSelect && e.shiftKey && this.lastSelection){
16498 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16500 this.select(item, this.multiSelect && e.ctrlKey);
16501 this.lastSelection = item;
16504 if(!this.tickable){
16505 e.preventDefault();
16513 * Get the number of selected nodes.
16516 getSelectionCount : function(){
16517 return this.selections.length;
16521 * Get the currently selected nodes.
16522 * @return {Array} An array of HTMLElements
16524 getSelectedNodes : function(){
16525 return this.selections;
16529 * Get the indexes of the selected nodes.
16532 getSelectedIndexes : function(){
16533 var indexes = [], s = this.selections;
16534 for(var i = 0, len = s.length; i < len; i++){
16535 indexes.push(s[i].nodeIndex);
16541 * Clear all selections
16542 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16544 clearSelections : function(suppressEvent){
16545 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16546 this.cmp.elements = this.selections;
16547 this.cmp.removeClass(this.selectedClass);
16548 this.selections = [];
16549 if(!suppressEvent){
16550 this.fireEvent("selectionchange", this, this.selections);
16556 * Returns true if the passed node is selected
16557 * @param {HTMLElement/Number} node The node or node index
16558 * @return {Boolean}
16560 isSelected : function(node){
16561 var s = this.selections;
16565 node = this.getNode(node);
16566 return s.indexOf(node) !== -1;
16571 * @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
16572 * @param {Boolean} keepExisting (optional) true to keep existing selections
16573 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16575 select : function(nodeInfo, keepExisting, suppressEvent){
16576 if(nodeInfo instanceof Array){
16578 this.clearSelections(true);
16580 for(var i = 0, len = nodeInfo.length; i < len; i++){
16581 this.select(nodeInfo[i], true, true);
16585 var node = this.getNode(nodeInfo);
16586 if(!node || this.isSelected(node)){
16587 return; // already selected.
16590 this.clearSelections(true);
16593 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16594 Roo.fly(node).addClass(this.selectedClass);
16595 this.selections.push(node);
16596 if(!suppressEvent){
16597 this.fireEvent("selectionchange", this, this.selections);
16605 * @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
16606 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16607 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16609 unselect : function(nodeInfo, keepExisting, suppressEvent)
16611 if(nodeInfo instanceof Array){
16612 Roo.each(this.selections, function(s) {
16613 this.unselect(s, nodeInfo);
16617 var node = this.getNode(nodeInfo);
16618 if(!node || !this.isSelected(node)){
16619 //Roo.log("not selected");
16620 return; // not selected.
16624 Roo.each(this.selections, function(s) {
16626 Roo.fly(node).removeClass(this.selectedClass);
16633 this.selections= ns;
16634 this.fireEvent("selectionchange", this, this.selections);
16638 * Gets a template node.
16639 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16640 * @return {HTMLElement} The node or null if it wasn't found
16642 getNode : function(nodeInfo){
16643 if(typeof nodeInfo == "string"){
16644 return document.getElementById(nodeInfo);
16645 }else if(typeof nodeInfo == "number"){
16646 return this.nodes[nodeInfo];
16652 * Gets a range template nodes.
16653 * @param {Number} startIndex
16654 * @param {Number} endIndex
16655 * @return {Array} An array of nodes
16657 getNodes : function(start, end){
16658 var ns = this.nodes;
16659 start = start || 0;
16660 end = typeof end == "undefined" ? ns.length - 1 : end;
16663 for(var i = start; i <= end; i++){
16667 for(var i = start; i >= end; i--){
16675 * Finds the index of the passed node
16676 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16677 * @return {Number} The index of the node or -1
16679 indexOf : function(node){
16680 node = this.getNode(node);
16681 if(typeof node.nodeIndex == "number"){
16682 return node.nodeIndex;
16684 var ns = this.nodes;
16685 for(var i = 0, len = ns.length; i < len; i++){
16696 * based on jquery fullcalendar
16700 Roo.bootstrap = Roo.bootstrap || {};
16702 * @class Roo.bootstrap.Calendar
16703 * @extends Roo.bootstrap.Component
16704 * Bootstrap Calendar class
16705 * @cfg {Boolean} loadMask (true|false) default false
16706 * @cfg {Object} header generate the user specific header of the calendar, default false
16709 * Create a new Container
16710 * @param {Object} config The config object
16715 Roo.bootstrap.Calendar = function(config){
16716 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16720 * Fires when a date is selected
16721 * @param {DatePicker} this
16722 * @param {Date} date The selected date
16726 * @event monthchange
16727 * Fires when the displayed month changes
16728 * @param {DatePicker} this
16729 * @param {Date} date The selected month
16731 'monthchange': true,
16733 * @event evententer
16734 * Fires when mouse over an event
16735 * @param {Calendar} this
16736 * @param {event} Event
16738 'evententer': true,
16740 * @event eventleave
16741 * Fires when the mouse leaves an
16742 * @param {Calendar} this
16745 'eventleave': true,
16747 * @event eventclick
16748 * Fires when the mouse click an
16749 * @param {Calendar} this
16758 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16761 * @cfg {Number} startDay
16762 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16770 getAutoCreate : function(){
16773 var fc_button = function(name, corner, style, content ) {
16774 return Roo.apply({},{
16776 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16778 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16781 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16792 style : 'width:100%',
16799 cls : 'fc-header-left',
16801 fc_button('prev', 'left', 'arrow', '‹' ),
16802 fc_button('next', 'right', 'arrow', '›' ),
16803 { tag: 'span', cls: 'fc-header-space' },
16804 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16812 cls : 'fc-header-center',
16816 cls: 'fc-header-title',
16819 html : 'month / year'
16827 cls : 'fc-header-right',
16829 /* fc_button('month', 'left', '', 'month' ),
16830 fc_button('week', '', '', 'week' ),
16831 fc_button('day', 'right', '', 'day' )
16843 header = this.header;
16846 var cal_heads = function() {
16848 // fixme - handle this.
16850 for (var i =0; i < Date.dayNames.length; i++) {
16851 var d = Date.dayNames[i];
16854 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16855 html : d.substring(0,3)
16859 ret[0].cls += ' fc-first';
16860 ret[6].cls += ' fc-last';
16863 var cal_cell = function(n) {
16866 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16871 cls: 'fc-day-number',
16875 cls: 'fc-day-content',
16879 style: 'position: relative;' // height: 17px;
16891 var cal_rows = function() {
16894 for (var r = 0; r < 6; r++) {
16901 for (var i =0; i < Date.dayNames.length; i++) {
16902 var d = Date.dayNames[i];
16903 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16906 row.cn[0].cls+=' fc-first';
16907 row.cn[0].cn[0].style = 'min-height:90px';
16908 row.cn[6].cls+=' fc-last';
16912 ret[0].cls += ' fc-first';
16913 ret[4].cls += ' fc-prev-last';
16914 ret[5].cls += ' fc-last';
16921 cls: 'fc-border-separate',
16922 style : 'width:100%',
16930 cls : 'fc-first fc-last',
16948 cls : 'fc-content',
16949 style : "position: relative;",
16952 cls : 'fc-view fc-view-month fc-grid',
16953 style : 'position: relative',
16954 unselectable : 'on',
16957 cls : 'fc-event-container',
16958 style : 'position:absolute;z-index:8;top:0;left:0;'
16976 initEvents : function()
16979 throw "can not find store for calendar";
16985 style: "text-align:center",
16989 style: "background-color:white;width:50%;margin:250 auto",
16993 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17004 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17006 var size = this.el.select('.fc-content', true).first().getSize();
17007 this.maskEl.setSize(size.width, size.height);
17008 this.maskEl.enableDisplayMode("block");
17009 if(!this.loadMask){
17010 this.maskEl.hide();
17013 this.store = Roo.factory(this.store, Roo.data);
17014 this.store.on('load', this.onLoad, this);
17015 this.store.on('beforeload', this.onBeforeLoad, this);
17019 this.cells = this.el.select('.fc-day',true);
17020 //Roo.log(this.cells);
17021 this.textNodes = this.el.query('.fc-day-number');
17022 this.cells.addClassOnOver('fc-state-hover');
17024 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17025 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17026 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17027 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17029 this.on('monthchange', this.onMonthChange, this);
17031 this.update(new Date().clearTime());
17034 resize : function() {
17035 var sz = this.el.getSize();
17037 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17038 this.el.select('.fc-day-content div',true).setHeight(34);
17043 showPrevMonth : function(e){
17044 this.update(this.activeDate.add("mo", -1));
17046 showToday : function(e){
17047 this.update(new Date().clearTime());
17050 showNextMonth : function(e){
17051 this.update(this.activeDate.add("mo", 1));
17055 showPrevYear : function(){
17056 this.update(this.activeDate.add("y", -1));
17060 showNextYear : function(){
17061 this.update(this.activeDate.add("y", 1));
17066 update : function(date)
17068 var vd = this.activeDate;
17069 this.activeDate = date;
17070 // if(vd && this.el){
17071 // var t = date.getTime();
17072 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17073 // Roo.log('using add remove');
17075 // this.fireEvent('monthchange', this, date);
17077 // this.cells.removeClass("fc-state-highlight");
17078 // this.cells.each(function(c){
17079 // if(c.dateValue == t){
17080 // c.addClass("fc-state-highlight");
17081 // setTimeout(function(){
17082 // try{c.dom.firstChild.focus();}catch(e){}
17092 var days = date.getDaysInMonth();
17094 var firstOfMonth = date.getFirstDateOfMonth();
17095 var startingPos = firstOfMonth.getDay()-this.startDay;
17097 if(startingPos < this.startDay){
17101 var pm = date.add(Date.MONTH, -1);
17102 var prevStart = pm.getDaysInMonth()-startingPos;
17104 this.cells = this.el.select('.fc-day',true);
17105 this.textNodes = this.el.query('.fc-day-number');
17106 this.cells.addClassOnOver('fc-state-hover');
17108 var cells = this.cells.elements;
17109 var textEls = this.textNodes;
17111 Roo.each(cells, function(cell){
17112 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17115 days += startingPos;
17117 // convert everything to numbers so it's fast
17118 var day = 86400000;
17119 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17122 //Roo.log(prevStart);
17124 var today = new Date().clearTime().getTime();
17125 var sel = date.clearTime().getTime();
17126 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17127 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17128 var ddMatch = this.disabledDatesRE;
17129 var ddText = this.disabledDatesText;
17130 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17131 var ddaysText = this.disabledDaysText;
17132 var format = this.format;
17134 var setCellClass = function(cal, cell){
17138 //Roo.log('set Cell Class');
17140 var t = d.getTime();
17144 cell.dateValue = t;
17146 cell.className += " fc-today";
17147 cell.className += " fc-state-highlight";
17148 cell.title = cal.todayText;
17151 // disable highlight in other month..
17152 //cell.className += " fc-state-highlight";
17157 cell.className = " fc-state-disabled";
17158 cell.title = cal.minText;
17162 cell.className = " fc-state-disabled";
17163 cell.title = cal.maxText;
17167 if(ddays.indexOf(d.getDay()) != -1){
17168 cell.title = ddaysText;
17169 cell.className = " fc-state-disabled";
17172 if(ddMatch && format){
17173 var fvalue = d.dateFormat(format);
17174 if(ddMatch.test(fvalue)){
17175 cell.title = ddText.replace("%0", fvalue);
17176 cell.className = " fc-state-disabled";
17180 if (!cell.initialClassName) {
17181 cell.initialClassName = cell.dom.className;
17184 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17189 for(; i < startingPos; i++) {
17190 textEls[i].innerHTML = (++prevStart);
17191 d.setDate(d.getDate()+1);
17193 cells[i].className = "fc-past fc-other-month";
17194 setCellClass(this, cells[i]);
17199 for(; i < days; i++){
17200 intDay = i - startingPos + 1;
17201 textEls[i].innerHTML = (intDay);
17202 d.setDate(d.getDate()+1);
17204 cells[i].className = ''; // "x-date-active";
17205 setCellClass(this, cells[i]);
17209 for(; i < 42; i++) {
17210 textEls[i].innerHTML = (++extraDays);
17211 d.setDate(d.getDate()+1);
17213 cells[i].className = "fc-future fc-other-month";
17214 setCellClass(this, cells[i]);
17217 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17219 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17221 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17222 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17224 if(totalRows != 6){
17225 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17226 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17229 this.fireEvent('monthchange', this, date);
17233 if(!this.internalRender){
17234 var main = this.el.dom.firstChild;
17235 var w = main.offsetWidth;
17236 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17237 Roo.fly(main).setWidth(w);
17238 this.internalRender = true;
17239 // opera does not respect the auto grow header center column
17240 // then, after it gets a width opera refuses to recalculate
17241 // without a second pass
17242 if(Roo.isOpera && !this.secondPass){
17243 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17244 this.secondPass = true;
17245 this.update.defer(10, this, [date]);
17252 findCell : function(dt) {
17253 dt = dt.clearTime().getTime();
17255 this.cells.each(function(c){
17256 //Roo.log("check " +c.dateValue + '?=' + dt);
17257 if(c.dateValue == dt){
17267 findCells : function(ev) {
17268 var s = ev.start.clone().clearTime().getTime();
17270 var e= ev.end.clone().clearTime().getTime();
17273 this.cells.each(function(c){
17274 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17276 if(c.dateValue > e){
17279 if(c.dateValue < s){
17288 // findBestRow: function(cells)
17292 // for (var i =0 ; i < cells.length;i++) {
17293 // ret = Math.max(cells[i].rows || 0,ret);
17300 addItem : function(ev)
17302 // look for vertical location slot in
17303 var cells = this.findCells(ev);
17305 // ev.row = this.findBestRow(cells);
17307 // work out the location.
17311 for(var i =0; i < cells.length; i++) {
17313 cells[i].row = cells[0].row;
17316 cells[i].row = cells[i].row + 1;
17326 if (crow.start.getY() == cells[i].getY()) {
17328 crow.end = cells[i];
17345 cells[0].events.push(ev);
17347 this.calevents.push(ev);
17350 clearEvents: function() {
17352 if(!this.calevents){
17356 Roo.each(this.cells.elements, function(c){
17362 Roo.each(this.calevents, function(e) {
17363 Roo.each(e.els, function(el) {
17364 el.un('mouseenter' ,this.onEventEnter, this);
17365 el.un('mouseleave' ,this.onEventLeave, this);
17370 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17376 renderEvents: function()
17380 this.cells.each(function(c) {
17389 if(c.row != c.events.length){
17390 r = 4 - (4 - (c.row - c.events.length));
17393 c.events = ev.slice(0, r);
17394 c.more = ev.slice(r);
17396 if(c.more.length && c.more.length == 1){
17397 c.events.push(c.more.pop());
17400 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17404 this.cells.each(function(c) {
17406 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17409 for (var e = 0; e < c.events.length; e++){
17410 var ev = c.events[e];
17411 var rows = ev.rows;
17413 for(var i = 0; i < rows.length; i++) {
17415 // how many rows should it span..
17418 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17419 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17421 unselectable : "on",
17424 cls: 'fc-event-inner',
17428 // cls: 'fc-event-time',
17429 // html : cells.length > 1 ? '' : ev.time
17433 cls: 'fc-event-title',
17434 html : String.format('{0}', ev.title)
17441 cls: 'ui-resizable-handle ui-resizable-e',
17442 html : '  '
17449 cfg.cls += ' fc-event-start';
17451 if ((i+1) == rows.length) {
17452 cfg.cls += ' fc-event-end';
17455 var ctr = _this.el.select('.fc-event-container',true).first();
17456 var cg = ctr.createChild(cfg);
17458 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17459 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17461 var r = (c.more.length) ? 1 : 0;
17462 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17463 cg.setWidth(ebox.right - sbox.x -2);
17465 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17466 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17467 cg.on('click', _this.onEventClick, _this, ev);
17478 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17479 style : 'position: absolute',
17480 unselectable : "on",
17483 cls: 'fc-event-inner',
17487 cls: 'fc-event-title',
17495 cls: 'ui-resizable-handle ui-resizable-e',
17496 html : '  '
17502 var ctr = _this.el.select('.fc-event-container',true).first();
17503 var cg = ctr.createChild(cfg);
17505 var sbox = c.select('.fc-day-content',true).first().getBox();
17506 var ebox = c.select('.fc-day-content',true).first().getBox();
17508 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17509 cg.setWidth(ebox.right - sbox.x -2);
17511 cg.on('click', _this.onMoreEventClick, _this, c.more);
17521 onEventEnter: function (e, el,event,d) {
17522 this.fireEvent('evententer', this, el, event);
17525 onEventLeave: function (e, el,event,d) {
17526 this.fireEvent('eventleave', this, el, event);
17529 onEventClick: function (e, el,event,d) {
17530 this.fireEvent('eventclick', this, el, event);
17533 onMonthChange: function () {
17537 onMoreEventClick: function(e, el, more)
17541 this.calpopover.placement = 'right';
17542 this.calpopover.setTitle('More');
17544 this.calpopover.setContent('');
17546 var ctr = this.calpopover.el.select('.popover-content', true).first();
17548 Roo.each(more, function(m){
17550 cls : 'fc-event-hori fc-event-draggable',
17553 var cg = ctr.createChild(cfg);
17555 cg.on('click', _this.onEventClick, _this, m);
17558 this.calpopover.show(el);
17563 onLoad: function ()
17565 this.calevents = [];
17568 if(this.store.getCount() > 0){
17569 this.store.data.each(function(d){
17572 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17573 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17574 time : d.data.start_time,
17575 title : d.data.title,
17576 description : d.data.description,
17577 venue : d.data.venue
17582 this.renderEvents();
17584 if(this.calevents.length && this.loadMask){
17585 this.maskEl.hide();
17589 onBeforeLoad: function()
17591 this.clearEvents();
17593 this.maskEl.show();
17607 * @class Roo.bootstrap.Popover
17608 * @extends Roo.bootstrap.Component
17609 * Bootstrap Popover class
17610 * @cfg {String} html contents of the popover (or false to use children..)
17611 * @cfg {String} title of popover (or false to hide)
17612 * @cfg {String} placement how it is placed
17613 * @cfg {String} trigger click || hover (or false to trigger manually)
17614 * @cfg {String} over what (parent or false to trigger manually.)
17615 * @cfg {Number} delay - delay before showing
17618 * Create a new Popover
17619 * @param {Object} config The config object
17622 Roo.bootstrap.Popover = function(config){
17623 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17629 * After the popover show
17631 * @param {Roo.bootstrap.Popover} this
17636 * After the popover hide
17638 * @param {Roo.bootstrap.Popover} this
17644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17646 title: 'Fill in a title',
17649 placement : 'right',
17650 trigger : 'hover', // hover
17656 can_build_overlaid : false,
17658 getChildContainer : function()
17660 return this.el.select('.popover-content',true).first();
17663 getAutoCreate : function(){
17666 cls : 'popover roo-dynamic',
17667 style: 'display:block',
17673 cls : 'popover-inner',
17677 cls: 'popover-title popover-header',
17681 cls : 'popover-content popover-body',
17692 setTitle: function(str)
17695 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17697 setContent: function(str)
17700 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17702 // as it get's added to the bottom of the page.
17703 onRender : function(ct, position)
17705 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17707 var cfg = Roo.apply({}, this.getAutoCreate());
17711 cfg.cls += ' ' + this.cls;
17714 cfg.style = this.style;
17716 //Roo.log("adding to ");
17717 this.el = Roo.get(document.body).createChild(cfg, position);
17718 // Roo.log(this.el);
17723 initEvents : function()
17725 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17726 this.el.enableDisplayMode('block');
17728 if (this.over === false) {
17731 if (this.triggers === false) {
17734 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17735 var triggers = this.trigger ? this.trigger.split(' ') : [];
17736 Roo.each(triggers, function(trigger) {
17738 if (trigger == 'click') {
17739 on_el.on('click', this.toggle, this);
17740 } else if (trigger != 'manual') {
17741 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17742 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17744 on_el.on(eventIn ,this.enter, this);
17745 on_el.on(eventOut, this.leave, this);
17756 toggle : function () {
17757 this.hoverState == 'in' ? this.leave() : this.enter();
17760 enter : function () {
17762 clearTimeout(this.timeout);
17764 this.hoverState = 'in';
17766 if (!this.delay || !this.delay.show) {
17771 this.timeout = setTimeout(function () {
17772 if (_t.hoverState == 'in') {
17775 }, this.delay.show)
17778 leave : function() {
17779 clearTimeout(this.timeout);
17781 this.hoverState = 'out';
17783 if (!this.delay || !this.delay.hide) {
17788 this.timeout = setTimeout(function () {
17789 if (_t.hoverState == 'out') {
17792 }, this.delay.hide)
17795 show : function (on_el)
17798 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17802 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17803 if (this.html !== false) {
17804 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17806 this.el.removeClass([
17807 'fade','top','bottom', 'left', 'right','in',
17808 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17810 if (!this.title.length) {
17811 this.el.select('.popover-title',true).hide();
17814 var placement = typeof this.placement == 'function' ?
17815 this.placement.call(this, this.el, on_el) :
17818 var autoToken = /\s?auto?\s?/i;
17819 var autoPlace = autoToken.test(placement);
17821 placement = placement.replace(autoToken, '') || 'top';
17825 //this.el.setXY([0,0]);
17827 this.el.dom.style.display='block';
17828 this.el.addClass(placement);
17830 //this.el.appendTo(on_el);
17832 var p = this.getPosition();
17833 var box = this.el.getBox();
17838 var align = Roo.bootstrap.Popover.alignment[placement];
17841 this.el.alignTo(on_el, align[0],align[1]);
17842 //var arrow = this.el.select('.arrow',true).first();
17843 //arrow.set(align[2],
17845 this.el.addClass('in');
17848 if (this.el.hasClass('fade')) {
17852 this.hoverState = 'in';
17854 this.fireEvent('show', this);
17859 this.el.setXY([0,0]);
17860 this.el.removeClass('in');
17862 this.hoverState = null;
17864 this.fireEvent('hide', this);
17869 Roo.bootstrap.Popover.alignment = {
17870 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17871 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17872 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17873 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17884 * @class Roo.bootstrap.Progress
17885 * @extends Roo.bootstrap.Component
17886 * Bootstrap Progress class
17887 * @cfg {Boolean} striped striped of the progress bar
17888 * @cfg {Boolean} active animated of the progress bar
17892 * Create a new Progress
17893 * @param {Object} config The config object
17896 Roo.bootstrap.Progress = function(config){
17897 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17900 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17905 getAutoCreate : function(){
17913 cfg.cls += ' progress-striped';
17917 cfg.cls += ' active';
17936 * @class Roo.bootstrap.ProgressBar
17937 * @extends Roo.bootstrap.Component
17938 * Bootstrap ProgressBar class
17939 * @cfg {Number} aria_valuenow aria-value now
17940 * @cfg {Number} aria_valuemin aria-value min
17941 * @cfg {Number} aria_valuemax aria-value max
17942 * @cfg {String} label label for the progress bar
17943 * @cfg {String} panel (success | info | warning | danger )
17944 * @cfg {String} role role of the progress bar
17945 * @cfg {String} sr_only text
17949 * Create a new ProgressBar
17950 * @param {Object} config The config object
17953 Roo.bootstrap.ProgressBar = function(config){
17954 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17957 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17961 aria_valuemax : 100,
17967 getAutoCreate : function()
17972 cls: 'progress-bar',
17973 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17985 cfg.role = this.role;
17988 if(this.aria_valuenow){
17989 cfg['aria-valuenow'] = this.aria_valuenow;
17992 if(this.aria_valuemin){
17993 cfg['aria-valuemin'] = this.aria_valuemin;
17996 if(this.aria_valuemax){
17997 cfg['aria-valuemax'] = this.aria_valuemax;
18000 if(this.label && !this.sr_only){
18001 cfg.html = this.label;
18005 cfg.cls += ' progress-bar-' + this.panel;
18011 update : function(aria_valuenow)
18013 this.aria_valuenow = aria_valuenow;
18015 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18030 * @class Roo.bootstrap.TabGroup
18031 * @extends Roo.bootstrap.Column
18032 * Bootstrap Column class
18033 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18034 * @cfg {Boolean} carousel true to make the group behave like a carousel
18035 * @cfg {Boolean} bullets show bullets for the panels
18036 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18037 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18038 * @cfg {Boolean} showarrow (true|false) show arrow default true
18041 * Create a new TabGroup
18042 * @param {Object} config The config object
18045 Roo.bootstrap.TabGroup = function(config){
18046 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18048 this.navId = Roo.id();
18051 Roo.bootstrap.TabGroup.register(this);
18055 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18058 transition : false,
18063 slideOnTouch : false,
18066 getAutoCreate : function()
18068 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18070 cfg.cls += ' tab-content';
18072 if (this.carousel) {
18073 cfg.cls += ' carousel slide';
18076 cls : 'carousel-inner',
18080 if(this.bullets && !Roo.isTouch){
18083 cls : 'carousel-bullets',
18087 if(this.bullets_cls){
18088 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18095 cfg.cn[0].cn.push(bullets);
18098 if(this.showarrow){
18099 cfg.cn[0].cn.push({
18101 class : 'carousel-arrow',
18105 class : 'carousel-prev',
18109 class : 'fa fa-chevron-left'
18115 class : 'carousel-next',
18119 class : 'fa fa-chevron-right'
18132 initEvents: function()
18134 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18135 // this.el.on("touchstart", this.onTouchStart, this);
18138 if(this.autoslide){
18141 this.slideFn = window.setInterval(function() {
18142 _this.showPanelNext();
18146 if(this.showarrow){
18147 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18148 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18154 // onTouchStart : function(e, el, o)
18156 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18160 // this.showPanelNext();
18164 getChildContainer : function()
18166 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18170 * register a Navigation item
18171 * @param {Roo.bootstrap.NavItem} the navitem to add
18173 register : function(item)
18175 this.tabs.push( item);
18176 item.navId = this.navId; // not really needed..
18181 getActivePanel : function()
18184 Roo.each(this.tabs, function(t) {
18194 getPanelByName : function(n)
18197 Roo.each(this.tabs, function(t) {
18198 if (t.tabId == n) {
18206 indexOfPanel : function(p)
18209 Roo.each(this.tabs, function(t,i) {
18210 if (t.tabId == p.tabId) {
18219 * show a specific panel
18220 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18221 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18223 showPanel : function (pan)
18225 if(this.transition || typeof(pan) == 'undefined'){
18226 Roo.log("waiting for the transitionend");
18230 if (typeof(pan) == 'number') {
18231 pan = this.tabs[pan];
18234 if (typeof(pan) == 'string') {
18235 pan = this.getPanelByName(pan);
18238 var cur = this.getActivePanel();
18241 Roo.log('pan or acitve pan is undefined');
18245 if (pan.tabId == this.getActivePanel().tabId) {
18249 if (false === cur.fireEvent('beforedeactivate')) {
18253 if(this.bullets > 0 && !Roo.isTouch){
18254 this.setActiveBullet(this.indexOfPanel(pan));
18257 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18259 this.transition = true;
18260 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18261 var lr = dir == 'next' ? 'left' : 'right';
18262 pan.el.addClass(dir); // or prev
18263 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18264 cur.el.addClass(lr); // or right
18265 pan.el.addClass(lr);
18268 cur.el.on('transitionend', function() {
18269 Roo.log("trans end?");
18271 pan.el.removeClass([lr,dir]);
18272 pan.setActive(true);
18274 cur.el.removeClass([lr]);
18275 cur.setActive(false);
18277 _this.transition = false;
18279 }, this, { single: true } );
18284 cur.setActive(false);
18285 pan.setActive(true);
18290 showPanelNext : function()
18292 var i = this.indexOfPanel(this.getActivePanel());
18294 if (i >= this.tabs.length - 1 && !this.autoslide) {
18298 if (i >= this.tabs.length - 1 && this.autoslide) {
18302 this.showPanel(this.tabs[i+1]);
18305 showPanelPrev : function()
18307 var i = this.indexOfPanel(this.getActivePanel());
18309 if (i < 1 && !this.autoslide) {
18313 if (i < 1 && this.autoslide) {
18314 i = this.tabs.length;
18317 this.showPanel(this.tabs[i-1]);
18321 addBullet: function()
18323 if(!this.bullets || Roo.isTouch){
18326 var ctr = this.el.select('.carousel-bullets',true).first();
18327 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18328 var bullet = ctr.createChild({
18329 cls : 'bullet bullet-' + i
18330 },ctr.dom.lastChild);
18335 bullet.on('click', (function(e, el, o, ii, t){
18337 e.preventDefault();
18339 this.showPanel(ii);
18341 if(this.autoslide && this.slideFn){
18342 clearInterval(this.slideFn);
18343 this.slideFn = window.setInterval(function() {
18344 _this.showPanelNext();
18348 }).createDelegate(this, [i, bullet], true));
18353 setActiveBullet : function(i)
18359 Roo.each(this.el.select('.bullet', true).elements, function(el){
18360 el.removeClass('selected');
18363 var bullet = this.el.select('.bullet-' + i, true).first();
18369 bullet.addClass('selected');
18380 Roo.apply(Roo.bootstrap.TabGroup, {
18384 * register a Navigation Group
18385 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18387 register : function(navgrp)
18389 this.groups[navgrp.navId] = navgrp;
18393 * fetch a Navigation Group based on the navigation ID
18394 * if one does not exist , it will get created.
18395 * @param {string} the navgroup to add
18396 * @returns {Roo.bootstrap.NavGroup} the navgroup
18398 get: function(navId) {
18399 if (typeof(this.groups[navId]) == 'undefined') {
18400 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18402 return this.groups[navId] ;
18417 * @class Roo.bootstrap.TabPanel
18418 * @extends Roo.bootstrap.Component
18419 * Bootstrap TabPanel class
18420 * @cfg {Boolean} active panel active
18421 * @cfg {String} html panel content
18422 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18423 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18424 * @cfg {String} href click to link..
18428 * Create a new TabPanel
18429 * @param {Object} config The config object
18432 Roo.bootstrap.TabPanel = function(config){
18433 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18437 * Fires when the active status changes
18438 * @param {Roo.bootstrap.TabPanel} this
18439 * @param {Boolean} state the new state
18444 * @event beforedeactivate
18445 * Fires before a tab is de-activated - can be used to do validation on a form.
18446 * @param {Roo.bootstrap.TabPanel} this
18447 * @return {Boolean} false if there is an error
18450 'beforedeactivate': true
18453 this.tabId = this.tabId || Roo.id();
18457 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18465 getAutoCreate : function(){
18468 // item is needed for carousel - not sure if it has any effect otherwise
18469 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18470 html: this.html || ''
18474 cfg.cls += ' active';
18478 cfg.tabId = this.tabId;
18485 initEvents: function()
18487 var p = this.parent();
18489 this.navId = this.navId || p.navId;
18491 if (typeof(this.navId) != 'undefined') {
18492 // not really needed.. but just in case.. parent should be a NavGroup.
18493 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18497 var i = tg.tabs.length - 1;
18499 if(this.active && tg.bullets > 0 && i < tg.bullets){
18500 tg.setActiveBullet(i);
18504 this.el.on('click', this.onClick, this);
18507 this.el.on("touchstart", this.onTouchStart, this);
18508 this.el.on("touchmove", this.onTouchMove, this);
18509 this.el.on("touchend", this.onTouchEnd, this);
18514 onRender : function(ct, position)
18516 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18519 setActive : function(state)
18521 Roo.log("panel - set active " + this.tabId + "=" + state);
18523 this.active = state;
18525 this.el.removeClass('active');
18527 } else if (!this.el.hasClass('active')) {
18528 this.el.addClass('active');
18531 this.fireEvent('changed', this, state);
18534 onClick : function(e)
18536 e.preventDefault();
18538 if(!this.href.length){
18542 window.location.href = this.href;
18551 onTouchStart : function(e)
18553 this.swiping = false;
18555 this.startX = e.browserEvent.touches[0].clientX;
18556 this.startY = e.browserEvent.touches[0].clientY;
18559 onTouchMove : function(e)
18561 this.swiping = true;
18563 this.endX = e.browserEvent.touches[0].clientX;
18564 this.endY = e.browserEvent.touches[0].clientY;
18567 onTouchEnd : function(e)
18574 var tabGroup = this.parent();
18576 if(this.endX > this.startX){ // swiping right
18577 tabGroup.showPanelPrev();
18581 if(this.startX > this.endX){ // swiping left
18582 tabGroup.showPanelNext();
18601 * @class Roo.bootstrap.DateField
18602 * @extends Roo.bootstrap.Input
18603 * Bootstrap DateField class
18604 * @cfg {Number} weekStart default 0
18605 * @cfg {String} viewMode default empty, (months|years)
18606 * @cfg {String} minViewMode default empty, (months|years)
18607 * @cfg {Number} startDate default -Infinity
18608 * @cfg {Number} endDate default Infinity
18609 * @cfg {Boolean} todayHighlight default false
18610 * @cfg {Boolean} todayBtn default false
18611 * @cfg {Boolean} calendarWeeks default false
18612 * @cfg {Object} daysOfWeekDisabled default empty
18613 * @cfg {Boolean} singleMode default false (true | false)
18615 * @cfg {Boolean} keyboardNavigation default true
18616 * @cfg {String} language default en
18619 * Create a new DateField
18620 * @param {Object} config The config object
18623 Roo.bootstrap.DateField = function(config){
18624 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18628 * Fires when this field show.
18629 * @param {Roo.bootstrap.DateField} this
18630 * @param {Mixed} date The date value
18635 * Fires when this field hide.
18636 * @param {Roo.bootstrap.DateField} this
18637 * @param {Mixed} date The date value
18642 * Fires when select a date.
18643 * @param {Roo.bootstrap.DateField} this
18644 * @param {Mixed} date The date value
18648 * @event beforeselect
18649 * Fires when before select a date.
18650 * @param {Roo.bootstrap.DateField} this
18651 * @param {Mixed} date The date value
18653 beforeselect : true
18657 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18660 * @cfg {String} format
18661 * The default date format string which can be overriden for localization support. The format must be
18662 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18666 * @cfg {String} altFormats
18667 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18668 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18670 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18678 todayHighlight : false,
18684 keyboardNavigation: true,
18686 calendarWeeks: false,
18688 startDate: -Infinity,
18692 daysOfWeekDisabled: [],
18696 singleMode : false,
18698 UTCDate: function()
18700 return new Date(Date.UTC.apply(Date, arguments));
18703 UTCToday: function()
18705 var today = new Date();
18706 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18709 getDate: function() {
18710 var d = this.getUTCDate();
18711 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18714 getUTCDate: function() {
18718 setDate: function(d) {
18719 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18722 setUTCDate: function(d) {
18724 this.setValue(this.formatDate(this.date));
18727 onRender: function(ct, position)
18730 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18732 this.language = this.language || 'en';
18733 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18734 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18736 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18737 this.format = this.format || 'm/d/y';
18738 this.isInline = false;
18739 this.isInput = true;
18740 this.component = this.el.select('.add-on', true).first() || false;
18741 this.component = (this.component && this.component.length === 0) ? false : this.component;
18742 this.hasInput = this.component && this.inputEl().length;
18744 if (typeof(this.minViewMode === 'string')) {
18745 switch (this.minViewMode) {
18747 this.minViewMode = 1;
18750 this.minViewMode = 2;
18753 this.minViewMode = 0;
18758 if (typeof(this.viewMode === 'string')) {
18759 switch (this.viewMode) {
18772 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18774 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18776 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18778 this.picker().on('mousedown', this.onMousedown, this);
18779 this.picker().on('click', this.onClick, this);
18781 this.picker().addClass('datepicker-dropdown');
18783 this.startViewMode = this.viewMode;
18785 if(this.singleMode){
18786 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18787 v.setVisibilityMode(Roo.Element.DISPLAY);
18791 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18792 v.setStyle('width', '189px');
18796 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18797 if(!this.calendarWeeks){
18802 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18803 v.attr('colspan', function(i, val){
18804 return parseInt(val) + 1;
18809 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18811 this.setStartDate(this.startDate);
18812 this.setEndDate(this.endDate);
18814 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18821 if(this.isInline) {
18826 picker : function()
18828 return this.pickerEl;
18829 // return this.el.select('.datepicker', true).first();
18832 fillDow: function()
18834 var dowCnt = this.weekStart;
18843 if(this.calendarWeeks){
18851 while (dowCnt < this.weekStart + 7) {
18855 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18859 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18862 fillMonths: function()
18865 var months = this.picker().select('>.datepicker-months td', true).first();
18867 months.dom.innerHTML = '';
18873 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18876 months.createChild(month);
18883 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;
18885 if (this.date < this.startDate) {
18886 this.viewDate = new Date(this.startDate);
18887 } else if (this.date > this.endDate) {
18888 this.viewDate = new Date(this.endDate);
18890 this.viewDate = new Date(this.date);
18898 var d = new Date(this.viewDate),
18899 year = d.getUTCFullYear(),
18900 month = d.getUTCMonth(),
18901 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18902 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18903 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18904 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18905 currentDate = this.date && this.date.valueOf(),
18906 today = this.UTCToday();
18908 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18910 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18912 // this.picker.select('>tfoot th.today').
18913 // .text(dates[this.language].today)
18914 // .toggle(this.todayBtn !== false);
18916 this.updateNavArrows();
18919 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18921 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18923 prevMonth.setUTCDate(day);
18925 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18927 var nextMonth = new Date(prevMonth);
18929 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18931 nextMonth = nextMonth.valueOf();
18933 var fillMonths = false;
18935 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18937 while(prevMonth.valueOf() <= nextMonth) {
18940 if (prevMonth.getUTCDay() === this.weekStart) {
18942 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18950 if(this.calendarWeeks){
18951 // ISO 8601: First week contains first thursday.
18952 // ISO also states week starts on Monday, but we can be more abstract here.
18954 // Start of current week: based on weekstart/current date
18955 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18956 // Thursday of this week
18957 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18958 // First Thursday of year, year from thursday
18959 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18960 // Calendar week: ms between thursdays, div ms per day, div 7 days
18961 calWeek = (th - yth) / 864e5 / 7 + 1;
18963 fillMonths.cn.push({
18971 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18973 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18976 if (this.todayHighlight &&
18977 prevMonth.getUTCFullYear() == today.getFullYear() &&
18978 prevMonth.getUTCMonth() == today.getMonth() &&
18979 prevMonth.getUTCDate() == today.getDate()) {
18980 clsName += ' today';
18983 if (currentDate && prevMonth.valueOf() === currentDate) {
18984 clsName += ' active';
18987 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18988 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18989 clsName += ' disabled';
18992 fillMonths.cn.push({
18994 cls: 'day ' + clsName,
18995 html: prevMonth.getDate()
18998 prevMonth.setDate(prevMonth.getDate()+1);
19001 var currentYear = this.date && this.date.getUTCFullYear();
19002 var currentMonth = this.date && this.date.getUTCMonth();
19004 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19006 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19007 v.removeClass('active');
19009 if(currentYear === year && k === currentMonth){
19010 v.addClass('active');
19013 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19014 v.addClass('disabled');
19020 year = parseInt(year/10, 10) * 10;
19022 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19024 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19027 for (var i = -1; i < 11; i++) {
19028 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19030 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19038 showMode: function(dir)
19041 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19044 Roo.each(this.picker().select('>div',true).elements, function(v){
19045 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19048 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19053 if(this.isInline) {
19057 this.picker().removeClass(['bottom', 'top']);
19059 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19061 * place to the top of element!
19065 this.picker().addClass('top');
19066 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19071 this.picker().addClass('bottom');
19073 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19076 parseDate : function(value)
19078 if(!value || value instanceof Date){
19081 var v = Date.parseDate(value, this.format);
19082 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19083 v = Date.parseDate(value, 'Y-m-d');
19085 if(!v && this.altFormats){
19086 if(!this.altFormatsArray){
19087 this.altFormatsArray = this.altFormats.split("|");
19089 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19090 v = Date.parseDate(value, this.altFormatsArray[i]);
19096 formatDate : function(date, fmt)
19098 return (!date || !(date instanceof Date)) ?
19099 date : date.dateFormat(fmt || this.format);
19102 onFocus : function()
19104 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19108 onBlur : function()
19110 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19112 var d = this.inputEl().getValue();
19119 showPopup : function()
19121 this.picker().show();
19125 this.fireEvent('showpopup', this, this.date);
19128 hidePopup : function()
19130 if(this.isInline) {
19133 this.picker().hide();
19134 this.viewMode = this.startViewMode;
19137 this.fireEvent('hidepopup', this, this.date);
19141 onMousedown: function(e)
19143 e.stopPropagation();
19144 e.preventDefault();
19149 Roo.bootstrap.DateField.superclass.keyup.call(this);
19153 setValue: function(v)
19155 if(this.fireEvent('beforeselect', this, v) !== false){
19156 var d = new Date(this.parseDate(v) ).clearTime();
19158 if(isNaN(d.getTime())){
19159 this.date = this.viewDate = '';
19160 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19164 v = this.formatDate(d);
19166 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19168 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19172 this.fireEvent('select', this, this.date);
19176 getValue: function()
19178 return this.formatDate(this.date);
19181 fireKey: function(e)
19183 if (!this.picker().isVisible()){
19184 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19190 var dateChanged = false,
19192 newDate, newViewDate;
19197 e.preventDefault();
19201 if (!this.keyboardNavigation) {
19204 dir = e.keyCode == 37 ? -1 : 1;
19207 newDate = this.moveYear(this.date, dir);
19208 newViewDate = this.moveYear(this.viewDate, dir);
19209 } else if (e.shiftKey){
19210 newDate = this.moveMonth(this.date, dir);
19211 newViewDate = this.moveMonth(this.viewDate, dir);
19213 newDate = new Date(this.date);
19214 newDate.setUTCDate(this.date.getUTCDate() + dir);
19215 newViewDate = new Date(this.viewDate);
19216 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19218 if (this.dateWithinRange(newDate)){
19219 this.date = newDate;
19220 this.viewDate = newViewDate;
19221 this.setValue(this.formatDate(this.date));
19223 e.preventDefault();
19224 dateChanged = true;
19229 if (!this.keyboardNavigation) {
19232 dir = e.keyCode == 38 ? -1 : 1;
19234 newDate = this.moveYear(this.date, dir);
19235 newViewDate = this.moveYear(this.viewDate, dir);
19236 } else if (e.shiftKey){
19237 newDate = this.moveMonth(this.date, dir);
19238 newViewDate = this.moveMonth(this.viewDate, dir);
19240 newDate = new Date(this.date);
19241 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19242 newViewDate = new Date(this.viewDate);
19243 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19245 if (this.dateWithinRange(newDate)){
19246 this.date = newDate;
19247 this.viewDate = newViewDate;
19248 this.setValue(this.formatDate(this.date));
19250 e.preventDefault();
19251 dateChanged = true;
19255 this.setValue(this.formatDate(this.date));
19257 e.preventDefault();
19260 this.setValue(this.formatDate(this.date));
19274 onClick: function(e)
19276 e.stopPropagation();
19277 e.preventDefault();
19279 var target = e.getTarget();
19281 if(target.nodeName.toLowerCase() === 'i'){
19282 target = Roo.get(target).dom.parentNode;
19285 var nodeName = target.nodeName;
19286 var className = target.className;
19287 var html = target.innerHTML;
19288 //Roo.log(nodeName);
19290 switch(nodeName.toLowerCase()) {
19292 switch(className) {
19298 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19299 switch(this.viewMode){
19301 this.viewDate = this.moveMonth(this.viewDate, dir);
19305 this.viewDate = this.moveYear(this.viewDate, dir);
19311 var date = new Date();
19312 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19314 this.setValue(this.formatDate(this.date));
19321 if (className.indexOf('disabled') < 0) {
19322 this.viewDate.setUTCDate(1);
19323 if (className.indexOf('month') > -1) {
19324 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19326 var year = parseInt(html, 10) || 0;
19327 this.viewDate.setUTCFullYear(year);
19331 if(this.singleMode){
19332 this.setValue(this.formatDate(this.viewDate));
19343 //Roo.log(className);
19344 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19345 var day = parseInt(html, 10) || 1;
19346 var year = this.viewDate.getUTCFullYear(),
19347 month = this.viewDate.getUTCMonth();
19349 if (className.indexOf('old') > -1) {
19356 } else if (className.indexOf('new') > -1) {
19364 //Roo.log([year,month,day]);
19365 this.date = this.UTCDate(year, month, day,0,0,0,0);
19366 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19368 //Roo.log(this.formatDate(this.date));
19369 this.setValue(this.formatDate(this.date));
19376 setStartDate: function(startDate)
19378 this.startDate = startDate || -Infinity;
19379 if (this.startDate !== -Infinity) {
19380 this.startDate = this.parseDate(this.startDate);
19383 this.updateNavArrows();
19386 setEndDate: function(endDate)
19388 this.endDate = endDate || Infinity;
19389 if (this.endDate !== Infinity) {
19390 this.endDate = this.parseDate(this.endDate);
19393 this.updateNavArrows();
19396 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19398 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19399 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19400 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19402 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19403 return parseInt(d, 10);
19406 this.updateNavArrows();
19409 updateNavArrows: function()
19411 if(this.singleMode){
19415 var d = new Date(this.viewDate),
19416 year = d.getUTCFullYear(),
19417 month = d.getUTCMonth();
19419 Roo.each(this.picker().select('.prev', true).elements, function(v){
19421 switch (this.viewMode) {
19424 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19430 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19437 Roo.each(this.picker().select('.next', true).elements, function(v){
19439 switch (this.viewMode) {
19442 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19448 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19456 moveMonth: function(date, dir)
19461 var new_date = new Date(date.valueOf()),
19462 day = new_date.getUTCDate(),
19463 month = new_date.getUTCMonth(),
19464 mag = Math.abs(dir),
19466 dir = dir > 0 ? 1 : -1;
19469 // If going back one month, make sure month is not current month
19470 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19472 return new_date.getUTCMonth() == month;
19474 // If going forward one month, make sure month is as expected
19475 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19477 return new_date.getUTCMonth() != new_month;
19479 new_month = month + dir;
19480 new_date.setUTCMonth(new_month);
19481 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19482 if (new_month < 0 || new_month > 11) {
19483 new_month = (new_month + 12) % 12;
19486 // For magnitudes >1, move one month at a time...
19487 for (var i=0; i<mag; i++) {
19488 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19489 new_date = this.moveMonth(new_date, dir);
19491 // ...then reset the day, keeping it in the new month
19492 new_month = new_date.getUTCMonth();
19493 new_date.setUTCDate(day);
19495 return new_month != new_date.getUTCMonth();
19498 // Common date-resetting loop -- if date is beyond end of month, make it
19501 new_date.setUTCDate(--day);
19502 new_date.setUTCMonth(new_month);
19507 moveYear: function(date, dir)
19509 return this.moveMonth(date, dir*12);
19512 dateWithinRange: function(date)
19514 return date >= this.startDate && date <= this.endDate;
19520 this.picker().remove();
19523 validateValue : function(value)
19525 if(this.getVisibilityEl().hasClass('hidden')){
19529 if(value.length < 1) {
19530 if(this.allowBlank){
19536 if(value.length < this.minLength){
19539 if(value.length > this.maxLength){
19543 var vt = Roo.form.VTypes;
19544 if(!vt[this.vtype](value, this)){
19548 if(typeof this.validator == "function"){
19549 var msg = this.validator(value);
19555 if(this.regex && !this.regex.test(value)){
19559 if(typeof(this.parseDate(value)) == 'undefined'){
19563 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19567 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19577 this.date = this.viewDate = '';
19579 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19584 Roo.apply(Roo.bootstrap.DateField, {
19595 html: '<i class="fa fa-arrow-left"/>'
19605 html: '<i class="fa fa-arrow-right"/>'
19647 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19648 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19649 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19650 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19651 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19664 navFnc: 'FullYear',
19669 navFnc: 'FullYear',
19674 Roo.apply(Roo.bootstrap.DateField, {
19678 cls: 'datepicker dropdown-menu roo-dynamic',
19682 cls: 'datepicker-days',
19686 cls: 'table-condensed',
19688 Roo.bootstrap.DateField.head,
19692 Roo.bootstrap.DateField.footer
19699 cls: 'datepicker-months',
19703 cls: 'table-condensed',
19705 Roo.bootstrap.DateField.head,
19706 Roo.bootstrap.DateField.content,
19707 Roo.bootstrap.DateField.footer
19714 cls: 'datepicker-years',
19718 cls: 'table-condensed',
19720 Roo.bootstrap.DateField.head,
19721 Roo.bootstrap.DateField.content,
19722 Roo.bootstrap.DateField.footer
19741 * @class Roo.bootstrap.TimeField
19742 * @extends Roo.bootstrap.Input
19743 * Bootstrap DateField class
19747 * Create a new TimeField
19748 * @param {Object} config The config object
19751 Roo.bootstrap.TimeField = function(config){
19752 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19756 * Fires when this field show.
19757 * @param {Roo.bootstrap.DateField} thisthis
19758 * @param {Mixed} date The date value
19763 * Fires when this field hide.
19764 * @param {Roo.bootstrap.DateField} this
19765 * @param {Mixed} date The date value
19770 * Fires when select a date.
19771 * @param {Roo.bootstrap.DateField} this
19772 * @param {Mixed} date The date value
19778 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19781 * @cfg {String} format
19782 * The default time format string which can be overriden for localization support. The format must be
19783 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19787 onRender: function(ct, position)
19790 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19792 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19794 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19796 this.pop = this.picker().select('>.datepicker-time',true).first();
19797 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19799 this.picker().on('mousedown', this.onMousedown, this);
19800 this.picker().on('click', this.onClick, this);
19802 this.picker().addClass('datepicker-dropdown');
19807 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19808 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19809 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19810 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19811 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19812 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19816 fireKey: function(e){
19817 if (!this.picker().isVisible()){
19818 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19824 e.preventDefault();
19832 this.onTogglePeriod();
19835 this.onIncrementMinutes();
19838 this.onDecrementMinutes();
19847 onClick: function(e) {
19848 e.stopPropagation();
19849 e.preventDefault();
19852 picker : function()
19854 return this.el.select('.datepicker', true).first();
19857 fillTime: function()
19859 var time = this.pop.select('tbody', true).first();
19861 time.dom.innerHTML = '';
19876 cls: 'hours-up glyphicon glyphicon-chevron-up'
19896 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19917 cls: 'timepicker-hour',
19932 cls: 'timepicker-minute',
19947 cls: 'btn btn-primary period',
19969 cls: 'hours-down glyphicon glyphicon-chevron-down'
19989 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20007 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20014 var hours = this.time.getHours();
20015 var minutes = this.time.getMinutes();
20028 hours = hours - 12;
20032 hours = '0' + hours;
20036 minutes = '0' + minutes;
20039 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20040 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20041 this.pop.select('button', true).first().dom.innerHTML = period;
20047 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20049 var cls = ['bottom'];
20051 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20058 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20063 this.picker().addClass(cls.join('-'));
20067 Roo.each(cls, function(c){
20069 _this.picker().setTop(_this.inputEl().getHeight());
20073 _this.picker().setTop(0 - _this.picker().getHeight());
20078 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20082 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20089 onFocus : function()
20091 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20095 onBlur : function()
20097 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20103 this.picker().show();
20108 this.fireEvent('show', this, this.date);
20113 this.picker().hide();
20116 this.fireEvent('hide', this, this.date);
20119 setTime : function()
20122 this.setValue(this.time.format(this.format));
20124 this.fireEvent('select', this, this.date);
20129 onMousedown: function(e){
20130 e.stopPropagation();
20131 e.preventDefault();
20134 onIncrementHours: function()
20136 Roo.log('onIncrementHours');
20137 this.time = this.time.add(Date.HOUR, 1);
20142 onDecrementHours: function()
20144 Roo.log('onDecrementHours');
20145 this.time = this.time.add(Date.HOUR, -1);
20149 onIncrementMinutes: function()
20151 Roo.log('onIncrementMinutes');
20152 this.time = this.time.add(Date.MINUTE, 1);
20156 onDecrementMinutes: function()
20158 Roo.log('onDecrementMinutes');
20159 this.time = this.time.add(Date.MINUTE, -1);
20163 onTogglePeriod: function()
20165 Roo.log('onTogglePeriod');
20166 this.time = this.time.add(Date.HOUR, 12);
20173 Roo.apply(Roo.bootstrap.TimeField, {
20203 cls: 'btn btn-info ok',
20215 Roo.apply(Roo.bootstrap.TimeField, {
20219 cls: 'datepicker dropdown-menu',
20223 cls: 'datepicker-time',
20227 cls: 'table-condensed',
20229 Roo.bootstrap.TimeField.content,
20230 Roo.bootstrap.TimeField.footer
20249 * @class Roo.bootstrap.MonthField
20250 * @extends Roo.bootstrap.Input
20251 * Bootstrap MonthField class
20253 * @cfg {String} language default en
20256 * Create a new MonthField
20257 * @param {Object} config The config object
20260 Roo.bootstrap.MonthField = function(config){
20261 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20266 * Fires when this field show.
20267 * @param {Roo.bootstrap.MonthField} this
20268 * @param {Mixed} date The date value
20273 * Fires when this field hide.
20274 * @param {Roo.bootstrap.MonthField} this
20275 * @param {Mixed} date The date value
20280 * Fires when select a date.
20281 * @param {Roo.bootstrap.MonthField} this
20282 * @param {String} oldvalue The old value
20283 * @param {String} newvalue The new value
20289 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20291 onRender: function(ct, position)
20294 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20296 this.language = this.language || 'en';
20297 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20298 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20300 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20301 this.isInline = false;
20302 this.isInput = true;
20303 this.component = this.el.select('.add-on', true).first() || false;
20304 this.component = (this.component && this.component.length === 0) ? false : this.component;
20305 this.hasInput = this.component && this.inputEL().length;
20307 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20309 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20311 this.picker().on('mousedown', this.onMousedown, this);
20312 this.picker().on('click', this.onClick, this);
20314 this.picker().addClass('datepicker-dropdown');
20316 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20317 v.setStyle('width', '189px');
20324 if(this.isInline) {
20330 setValue: function(v, suppressEvent)
20332 var o = this.getValue();
20334 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20338 if(suppressEvent !== true){
20339 this.fireEvent('select', this, o, v);
20344 getValue: function()
20349 onClick: function(e)
20351 e.stopPropagation();
20352 e.preventDefault();
20354 var target = e.getTarget();
20356 if(target.nodeName.toLowerCase() === 'i'){
20357 target = Roo.get(target).dom.parentNode;
20360 var nodeName = target.nodeName;
20361 var className = target.className;
20362 var html = target.innerHTML;
20364 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20368 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20370 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20376 picker : function()
20378 return this.pickerEl;
20381 fillMonths: function()
20384 var months = this.picker().select('>.datepicker-months td', true).first();
20386 months.dom.innerHTML = '';
20392 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20395 months.createChild(month);
20404 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20405 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20408 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20409 e.removeClass('active');
20411 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20412 e.addClass('active');
20419 if(this.isInline) {
20423 this.picker().removeClass(['bottom', 'top']);
20425 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20427 * place to the top of element!
20431 this.picker().addClass('top');
20432 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20437 this.picker().addClass('bottom');
20439 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20442 onFocus : function()
20444 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20448 onBlur : function()
20450 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20452 var d = this.inputEl().getValue();
20461 this.picker().show();
20462 this.picker().select('>.datepicker-months', true).first().show();
20466 this.fireEvent('show', this, this.date);
20471 if(this.isInline) {
20474 this.picker().hide();
20475 this.fireEvent('hide', this, this.date);
20479 onMousedown: function(e)
20481 e.stopPropagation();
20482 e.preventDefault();
20487 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20491 fireKey: function(e)
20493 if (!this.picker().isVisible()){
20494 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20505 e.preventDefault();
20509 dir = e.keyCode == 37 ? -1 : 1;
20511 this.vIndex = this.vIndex + dir;
20513 if(this.vIndex < 0){
20517 if(this.vIndex > 11){
20521 if(isNaN(this.vIndex)){
20525 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20531 dir = e.keyCode == 38 ? -1 : 1;
20533 this.vIndex = this.vIndex + dir * 4;
20535 if(this.vIndex < 0){
20539 if(this.vIndex > 11){
20543 if(isNaN(this.vIndex)){
20547 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20552 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20553 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20557 e.preventDefault();
20560 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20561 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577 this.picker().remove();
20582 Roo.apply(Roo.bootstrap.MonthField, {
20601 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20602 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20607 Roo.apply(Roo.bootstrap.MonthField, {
20611 cls: 'datepicker dropdown-menu roo-dynamic',
20615 cls: 'datepicker-months',
20619 cls: 'table-condensed',
20621 Roo.bootstrap.DateField.content
20641 * @class Roo.bootstrap.CheckBox
20642 * @extends Roo.bootstrap.Input
20643 * Bootstrap CheckBox class
20645 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20646 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20647 * @cfg {String} boxLabel The text that appears beside the checkbox
20648 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20649 * @cfg {Boolean} checked initnal the element
20650 * @cfg {Boolean} inline inline the element (default false)
20651 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20652 * @cfg {String} tooltip label tooltip
20655 * Create a new CheckBox
20656 * @param {Object} config The config object
20659 Roo.bootstrap.CheckBox = function(config){
20660 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20665 * Fires when the element is checked or unchecked.
20666 * @param {Roo.bootstrap.CheckBox} this This input
20667 * @param {Boolean} checked The new checked value
20672 * Fires when the element is click.
20673 * @param {Roo.bootstrap.CheckBox} this This input
20680 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20682 inputType: 'checkbox',
20691 getAutoCreate : function()
20693 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20699 cfg.cls = 'form-group ' + this.inputType; //input-group
20702 cfg.cls += ' ' + this.inputType + '-inline';
20708 type : this.inputType,
20709 value : this.inputValue,
20710 cls : 'roo-' + this.inputType, //'form-box',
20711 placeholder : this.placeholder || ''
20715 if(this.inputType != 'radio'){
20719 cls : 'roo-hidden-value',
20720 value : this.checked ? this.inputValue : this.valueOff
20725 if (this.weight) { // Validity check?
20726 cfg.cls += " " + this.inputType + "-" + this.weight;
20729 if (this.disabled) {
20730 input.disabled=true;
20734 input.checked = this.checked;
20739 input.name = this.name;
20741 if(this.inputType != 'radio'){
20742 hidden.name = this.name;
20743 input.name = '_hidden_' + this.name;
20748 input.cls += ' input-' + this.size;
20753 ['xs','sm','md','lg'].map(function(size){
20754 if (settings[size]) {
20755 cfg.cls += ' col-' + size + '-' + settings[size];
20759 var inputblock = input;
20761 if (this.before || this.after) {
20764 cls : 'input-group',
20769 inputblock.cn.push({
20771 cls : 'input-group-addon',
20776 inputblock.cn.push(input);
20778 if(this.inputType != 'radio'){
20779 inputblock.cn.push(hidden);
20783 inputblock.cn.push({
20785 cls : 'input-group-addon',
20792 if (align ==='left' && this.fieldLabel.length) {
20793 // Roo.log("left and has label");
20798 cls : 'control-label',
20799 html : this.fieldLabel
20809 if(this.labelWidth > 12){
20810 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20813 if(this.labelWidth < 13 && this.labelmd == 0){
20814 this.labelmd = this.labelWidth;
20817 if(this.labellg > 0){
20818 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20819 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20822 if(this.labelmd > 0){
20823 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20824 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20827 if(this.labelsm > 0){
20828 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20829 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20832 if(this.labelxs > 0){
20833 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20834 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20837 } else if ( this.fieldLabel.length) {
20838 // Roo.log(" label");
20842 tag: this.boxLabel ? 'span' : 'label',
20844 cls: 'control-label box-input-label',
20845 //cls : 'input-group-addon',
20846 html : this.fieldLabel
20855 // Roo.log(" no label && no align");
20856 cfg.cn = [ inputblock ] ;
20862 var boxLabelCfg = {
20864 //'for': id, // box label is handled by onclick - so no for...
20866 html: this.boxLabel
20870 boxLabelCfg.tooltip = this.tooltip;
20873 cfg.cn.push(boxLabelCfg);
20876 if(this.inputType != 'radio'){
20877 cfg.cn.push(hidden);
20885 * return the real input element.
20887 inputEl: function ()
20889 return this.el.select('input.roo-' + this.inputType,true).first();
20891 hiddenEl: function ()
20893 return this.el.select('input.roo-hidden-value',true).first();
20896 labelEl: function()
20898 return this.el.select('label.control-label',true).first();
20900 /* depricated... */
20904 return this.labelEl();
20907 boxLabelEl: function()
20909 return this.el.select('label.box-label',true).first();
20912 initEvents : function()
20914 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20916 this.inputEl().on('click', this.onClick, this);
20918 if (this.boxLabel) {
20919 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20922 this.startValue = this.getValue();
20925 Roo.bootstrap.CheckBox.register(this);
20929 onClick : function(e)
20931 if(this.fireEvent('click', this, e) !== false){
20932 this.setChecked(!this.checked);
20937 setChecked : function(state,suppressEvent)
20939 this.startValue = this.getValue();
20941 if(this.inputType == 'radio'){
20943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944 e.dom.checked = false;
20947 this.inputEl().dom.checked = true;
20949 this.inputEl().dom.value = this.inputValue;
20951 if(suppressEvent !== true){
20952 this.fireEvent('check', this, true);
20960 this.checked = state;
20962 this.inputEl().dom.checked = state;
20965 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20967 if(suppressEvent !== true){
20968 this.fireEvent('check', this, state);
20974 getValue : function()
20976 if(this.inputType == 'radio'){
20977 return this.getGroupValue();
20980 return this.hiddenEl().dom.value;
20984 getGroupValue : function()
20986 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20990 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20993 setValue : function(v,suppressEvent)
20995 if(this.inputType == 'radio'){
20996 this.setGroupValue(v, suppressEvent);
21000 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21005 setGroupValue : function(v, suppressEvent)
21007 this.startValue = this.getValue();
21009 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21010 e.dom.checked = false;
21012 if(e.dom.value == v){
21013 e.dom.checked = true;
21017 if(suppressEvent !== true){
21018 this.fireEvent('check', this, true);
21026 validate : function()
21028 if(this.getVisibilityEl().hasClass('hidden')){
21034 (this.inputType == 'radio' && this.validateRadio()) ||
21035 (this.inputType == 'checkbox' && this.validateCheckbox())
21041 this.markInvalid();
21045 validateRadio : function()
21047 if(this.getVisibilityEl().hasClass('hidden')){
21051 if(this.allowBlank){
21057 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21058 if(!e.dom.checked){
21070 validateCheckbox : function()
21073 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21074 //return (this.getValue() == this.inputValue) ? true : false;
21077 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21085 for(var i in group){
21086 if(group[i].el.isVisible(true)){
21094 for(var i in group){
21099 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21106 * Mark this field as valid
21108 markValid : function()
21112 this.fireEvent('valid', this);
21114 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21117 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21124 if(this.inputType == 'radio'){
21125 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21126 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21127 e.findParent('.form-group', false, true).addClass(_this.validClass);
21134 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21135 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21139 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21145 for(var i in group){
21146 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21147 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21152 * Mark this field as invalid
21153 * @param {String} msg The validation message
21155 markInvalid : function(msg)
21157 if(this.allowBlank){
21163 this.fireEvent('invalid', this, msg);
21165 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21168 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21172 label.markInvalid();
21175 if(this.inputType == 'radio'){
21176 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21177 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21178 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21185 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21186 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21190 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21196 for(var i in group){
21197 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21198 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21203 clearInvalid : function()
21205 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21207 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21209 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21211 if (label && label.iconEl) {
21212 label.iconEl.removeClass(label.validClass);
21213 label.iconEl.removeClass(label.invalidClass);
21217 disable : function()
21219 if(this.inputType != 'radio'){
21220 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21227 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21228 _this.getActionEl().addClass(this.disabledClass);
21229 e.dom.disabled = true;
21233 this.disabled = true;
21234 this.fireEvent("disable", this);
21238 enable : function()
21240 if(this.inputType != 'radio'){
21241 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21248 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21249 _this.getActionEl().removeClass(this.disabledClass);
21250 e.dom.disabled = false;
21254 this.disabled = false;
21255 this.fireEvent("enable", this);
21259 setBoxLabel : function(v)
21264 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21270 Roo.apply(Roo.bootstrap.CheckBox, {
21275 * register a CheckBox Group
21276 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21278 register : function(checkbox)
21280 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21281 this.groups[checkbox.groupId] = {};
21284 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21288 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21292 * fetch a CheckBox Group based on the group ID
21293 * @param {string} the group ID
21294 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21296 get: function(groupId) {
21297 if (typeof(this.groups[groupId]) == 'undefined') {
21301 return this.groups[groupId] ;
21314 * @class Roo.bootstrap.Radio
21315 * @extends Roo.bootstrap.Component
21316 * Bootstrap Radio class
21317 * @cfg {String} boxLabel - the label associated
21318 * @cfg {String} value - the value of radio
21321 * Create a new Radio
21322 * @param {Object} config The config object
21324 Roo.bootstrap.Radio = function(config){
21325 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21329 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21335 getAutoCreate : function()
21339 cls : 'form-group radio',
21344 html : this.boxLabel
21352 initEvents : function()
21354 this.parent().register(this);
21356 this.el.on('click', this.onClick, this);
21360 onClick : function(e)
21362 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21363 this.setChecked(true);
21367 setChecked : function(state, suppressEvent)
21369 this.parent().setValue(this.value, suppressEvent);
21373 setBoxLabel : function(v)
21378 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21393 * @class Roo.bootstrap.SecurePass
21394 * @extends Roo.bootstrap.Input
21395 * Bootstrap SecurePass class
21399 * Create a new SecurePass
21400 * @param {Object} config The config object
21403 Roo.bootstrap.SecurePass = function (config) {
21404 // these go here, so the translation tool can replace them..
21406 PwdEmpty: "Please type a password, and then retype it to confirm.",
21407 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21408 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21409 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21410 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21411 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21412 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21413 TooWeak: "Your password is Too Weak."
21415 this.meterLabel = "Password strength:";
21416 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21417 this.meterClass = [
21418 "roo-password-meter-tooweak",
21419 "roo-password-meter-weak",
21420 "roo-password-meter-medium",
21421 "roo-password-meter-strong",
21422 "roo-password-meter-grey"
21427 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21430 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21432 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21434 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21435 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21436 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21437 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21438 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21439 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21440 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21450 * @cfg {String/Object} Label for the strength meter (defaults to
21451 * 'Password strength:')
21456 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21457 * ['Weak', 'Medium', 'Strong'])
21460 pwdStrengths: false,
21473 initEvents: function ()
21475 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21477 if (this.el.is('input[type=password]') && Roo.isSafari) {
21478 this.el.on('keydown', this.SafariOnKeyDown, this);
21481 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21484 onRender: function (ct, position)
21486 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21487 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21488 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21490 this.trigger.createChild({
21495 cls: 'roo-password-meter-grey col-xs-12',
21498 //width: this.meterWidth + 'px'
21502 cls: 'roo-password-meter-text'
21508 if (this.hideTrigger) {
21509 this.trigger.setDisplayed(false);
21511 this.setSize(this.width || '', this.height || '');
21514 onDestroy: function ()
21516 if (this.trigger) {
21517 this.trigger.removeAllListeners();
21518 this.trigger.remove();
21521 this.wrap.remove();
21523 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21526 checkStrength: function ()
21528 var pwd = this.inputEl().getValue();
21529 if (pwd == this._lastPwd) {
21534 if (this.ClientSideStrongPassword(pwd)) {
21536 } else if (this.ClientSideMediumPassword(pwd)) {
21538 } else if (this.ClientSideWeakPassword(pwd)) {
21544 Roo.log('strength1: ' + strength);
21546 //var pm = this.trigger.child('div/div/div').dom;
21547 var pm = this.trigger.child('div/div');
21548 pm.removeClass(this.meterClass);
21549 pm.addClass(this.meterClass[strength]);
21552 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21554 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21556 this._lastPwd = pwd;
21560 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21562 this._lastPwd = '';
21564 var pm = this.trigger.child('div/div');
21565 pm.removeClass(this.meterClass);
21566 pm.addClass('roo-password-meter-grey');
21569 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21572 this.inputEl().dom.type='password';
21575 validateValue: function (value)
21578 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21581 if (value.length == 0) {
21582 if (this.allowBlank) {
21583 this.clearInvalid();
21587 this.markInvalid(this.errors.PwdEmpty);
21588 this.errorMsg = this.errors.PwdEmpty;
21596 if ('[\x21-\x7e]*'.match(value)) {
21597 this.markInvalid(this.errors.PwdBadChar);
21598 this.errorMsg = this.errors.PwdBadChar;
21601 if (value.length < 6) {
21602 this.markInvalid(this.errors.PwdShort);
21603 this.errorMsg = this.errors.PwdShort;
21606 if (value.length > 16) {
21607 this.markInvalid(this.errors.PwdLong);
21608 this.errorMsg = this.errors.PwdLong;
21612 if (this.ClientSideStrongPassword(value)) {
21614 } else if (this.ClientSideMediumPassword(value)) {
21616 } else if (this.ClientSideWeakPassword(value)) {
21623 if (strength < 2) {
21624 //this.markInvalid(this.errors.TooWeak);
21625 this.errorMsg = this.errors.TooWeak;
21630 console.log('strength2: ' + strength);
21632 //var pm = this.trigger.child('div/div/div').dom;
21634 var pm = this.trigger.child('div/div');
21635 pm.removeClass(this.meterClass);
21636 pm.addClass(this.meterClass[strength]);
21638 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21640 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21642 this.errorMsg = '';
21646 CharacterSetChecks: function (type)
21649 this.fResult = false;
21652 isctype: function (character, type)
21655 case this.kCapitalLetter:
21656 if (character >= 'A' && character <= 'Z') {
21661 case this.kSmallLetter:
21662 if (character >= 'a' && character <= 'z') {
21668 if (character >= '0' && character <= '9') {
21673 case this.kPunctuation:
21674 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21685 IsLongEnough: function (pwd, size)
21687 return !(pwd == null || isNaN(size) || pwd.length < size);
21690 SpansEnoughCharacterSets: function (word, nb)
21692 if (!this.IsLongEnough(word, nb))
21697 var characterSetChecks = new Array(
21698 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21699 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21702 for (var index = 0; index < word.length; ++index) {
21703 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21704 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21705 characterSetChecks[nCharSet].fResult = true;
21712 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21713 if (characterSetChecks[nCharSet].fResult) {
21718 if (nCharSets < nb) {
21724 ClientSideStrongPassword: function (pwd)
21726 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21729 ClientSideMediumPassword: function (pwd)
21731 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21734 ClientSideWeakPassword: function (pwd)
21736 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21739 })//<script type="text/javascript">
21742 * Based Ext JS Library 1.1.1
21743 * Copyright(c) 2006-2007, Ext JS, LLC.
21749 * @class Roo.HtmlEditorCore
21750 * @extends Roo.Component
21751 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21753 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21756 Roo.HtmlEditorCore = function(config){
21759 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21764 * @event initialize
21765 * Fires when the editor is fully initialized (including the iframe)
21766 * @param {Roo.HtmlEditorCore} this
21771 * Fires when the editor is first receives the focus. Any insertion must wait
21772 * until after this event.
21773 * @param {Roo.HtmlEditorCore} this
21777 * @event beforesync
21778 * Fires before the textarea is updated with content from the editor iframe. Return false
21779 * to cancel the sync.
21780 * @param {Roo.HtmlEditorCore} this
21781 * @param {String} html
21785 * @event beforepush
21786 * Fires before the iframe editor is updated with content from the textarea. Return false
21787 * to cancel the push.
21788 * @param {Roo.HtmlEditorCore} this
21789 * @param {String} html
21794 * Fires when the textarea is updated with content from the editor iframe.
21795 * @param {Roo.HtmlEditorCore} this
21796 * @param {String} html
21801 * Fires when the iframe editor is updated with content from the textarea.
21802 * @param {Roo.HtmlEditorCore} this
21803 * @param {String} html
21808 * @event editorevent
21809 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21810 * @param {Roo.HtmlEditorCore} this
21816 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21818 // defaults : white / black...
21819 this.applyBlacklists();
21826 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21830 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21836 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21841 * @cfg {Number} height (in pixels)
21845 * @cfg {Number} width (in pixels)
21850 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21853 stylesheets: false,
21858 // private properties
21859 validationEvent : false,
21861 initialized : false,
21863 sourceEditMode : false,
21864 onFocus : Roo.emptyFn,
21866 hideMode:'offsets',
21870 // blacklist + whitelisted elements..
21877 * Protected method that will not generally be called directly. It
21878 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21879 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21881 getDocMarkup : function(){
21885 // inherit styels from page...??
21886 if (this.stylesheets === false) {
21888 Roo.get(document.head).select('style').each(function(node) {
21889 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21892 Roo.get(document.head).select('link').each(function(node) {
21893 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21896 } else if (!this.stylesheets.length) {
21898 st = '<style type="text/css">' +
21899 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21902 st = '<style type="text/css">' +
21907 st += '<style type="text/css">' +
21908 'IMG { cursor: pointer } ' +
21911 var cls = 'roo-htmleditor-body';
21913 if(this.bodyCls.length){
21914 cls += ' ' + this.bodyCls;
21917 return '<html><head>' + st +
21918 //<style type="text/css">' +
21919 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21921 ' </head><body class="' + cls + '"></body></html>';
21925 onRender : function(ct, position)
21928 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21929 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21932 this.el.dom.style.border = '0 none';
21933 this.el.dom.setAttribute('tabIndex', -1);
21934 this.el.addClass('x-hidden hide');
21938 if(Roo.isIE){ // fix IE 1px bogus margin
21939 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21943 this.frameId = Roo.id();
21947 var iframe = this.owner.wrap.createChild({
21949 cls: 'form-control', // bootstrap..
21951 name: this.frameId,
21952 frameBorder : 'no',
21953 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21958 this.iframe = iframe.dom;
21960 this.assignDocWin();
21962 this.doc.designMode = 'on';
21965 this.doc.write(this.getDocMarkup());
21969 var task = { // must defer to wait for browser to be ready
21971 //console.log("run task?" + this.doc.readyState);
21972 this.assignDocWin();
21973 if(this.doc.body || this.doc.readyState == 'complete'){
21975 this.doc.designMode="on";
21979 Roo.TaskMgr.stop(task);
21980 this.initEditor.defer(10, this);
21987 Roo.TaskMgr.start(task);
21992 onResize : function(w, h)
21994 Roo.log('resize: ' +w + ',' + h );
21995 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21999 if(typeof w == 'number'){
22001 this.iframe.style.width = w + 'px';
22003 if(typeof h == 'number'){
22005 this.iframe.style.height = h + 'px';
22007 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22014 * Toggles the editor between standard and source edit mode.
22015 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22017 toggleSourceEdit : function(sourceEditMode){
22019 this.sourceEditMode = sourceEditMode === true;
22021 if(this.sourceEditMode){
22023 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22026 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22027 //this.iframe.className = '';
22030 //this.setSize(this.owner.wrap.getSize());
22031 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22038 * Protected method that will not generally be called directly. If you need/want
22039 * custom HTML cleanup, this is the method you should override.
22040 * @param {String} html The HTML to be cleaned
22041 * return {String} The cleaned HTML
22043 cleanHtml : function(html){
22044 html = String(html);
22045 if(html.length > 5){
22046 if(Roo.isSafari){ // strip safari nonsense
22047 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22050 if(html == ' '){
22057 * HTML Editor -> Textarea
22058 * Protected method that will not generally be called directly. Syncs the contents
22059 * of the editor iframe with the textarea.
22061 syncValue : function(){
22062 if(this.initialized){
22063 var bd = (this.doc.body || this.doc.documentElement);
22064 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22065 var html = bd.innerHTML;
22067 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22068 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22070 html = '<div style="'+m[0]+'">' + html + '</div>';
22073 html = this.cleanHtml(html);
22074 // fix up the special chars.. normaly like back quotes in word...
22075 // however we do not want to do this with chinese..
22076 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22077 var cc = b.charCodeAt();
22079 (cc >= 0x4E00 && cc < 0xA000 ) ||
22080 (cc >= 0x3400 && cc < 0x4E00 ) ||
22081 (cc >= 0xf900 && cc < 0xfb00 )
22087 if(this.owner.fireEvent('beforesync', this, html) !== false){
22088 this.el.dom.value = html;
22089 this.owner.fireEvent('sync', this, html);
22095 * Protected method that will not generally be called directly. Pushes the value of the textarea
22096 * into the iframe editor.
22098 pushValue : function(){
22099 if(this.initialized){
22100 var v = this.el.dom.value.trim();
22102 // if(v.length < 1){
22106 if(this.owner.fireEvent('beforepush', this, v) !== false){
22107 var d = (this.doc.body || this.doc.documentElement);
22109 this.cleanUpPaste();
22110 this.el.dom.value = d.innerHTML;
22111 this.owner.fireEvent('push', this, v);
22117 deferFocus : function(){
22118 this.focus.defer(10, this);
22122 focus : function(){
22123 if(this.win && !this.sourceEditMode){
22130 assignDocWin: function()
22132 var iframe = this.iframe;
22135 this.doc = iframe.contentWindow.document;
22136 this.win = iframe.contentWindow;
22138 // if (!Roo.get(this.frameId)) {
22141 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22142 // this.win = Roo.get(this.frameId).dom.contentWindow;
22144 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22148 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22149 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22154 initEditor : function(){
22155 //console.log("INIT EDITOR");
22156 this.assignDocWin();
22160 this.doc.designMode="on";
22162 this.doc.write(this.getDocMarkup());
22165 var dbody = (this.doc.body || this.doc.documentElement);
22166 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22167 // this copies styles from the containing element into thsi one..
22168 // not sure why we need all of this..
22169 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22171 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22172 //ss['background-attachment'] = 'fixed'; // w3c
22173 dbody.bgProperties = 'fixed'; // ie
22174 //Roo.DomHelper.applyStyles(dbody, ss);
22175 Roo.EventManager.on(this.doc, {
22176 //'mousedown': this.onEditorEvent,
22177 'mouseup': this.onEditorEvent,
22178 'dblclick': this.onEditorEvent,
22179 'click': this.onEditorEvent,
22180 'keyup': this.onEditorEvent,
22185 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22187 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22188 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22190 this.initialized = true;
22192 this.owner.fireEvent('initialize', this);
22197 onDestroy : function(){
22203 //for (var i =0; i < this.toolbars.length;i++) {
22204 // // fixme - ask toolbars for heights?
22205 // this.toolbars[i].onDestroy();
22208 //this.wrap.dom.innerHTML = '';
22209 //this.wrap.remove();
22214 onFirstFocus : function(){
22216 this.assignDocWin();
22219 this.activated = true;
22222 if(Roo.isGecko){ // prevent silly gecko errors
22224 var s = this.win.getSelection();
22225 if(!s.focusNode || s.focusNode.nodeType != 3){
22226 var r = s.getRangeAt(0);
22227 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22232 this.execCmd('useCSS', true);
22233 this.execCmd('styleWithCSS', false);
22236 this.owner.fireEvent('activate', this);
22240 adjustFont: function(btn){
22241 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22242 //if(Roo.isSafari){ // safari
22245 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22246 if(Roo.isSafari){ // safari
22247 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22248 v = (v < 10) ? 10 : v;
22249 v = (v > 48) ? 48 : v;
22250 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22255 v = Math.max(1, v+adjust);
22257 this.execCmd('FontSize', v );
22260 onEditorEvent : function(e)
22262 this.owner.fireEvent('editorevent', this, e);
22263 // this.updateToolbar();
22264 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22267 insertTag : function(tg)
22269 // could be a bit smarter... -> wrap the current selected tRoo..
22270 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22272 range = this.createRange(this.getSelection());
22273 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22274 wrappingNode.appendChild(range.extractContents());
22275 range.insertNode(wrappingNode);
22282 this.execCmd("formatblock", tg);
22286 insertText : function(txt)
22290 var range = this.createRange();
22291 range.deleteContents();
22292 //alert(Sender.getAttribute('label'));
22294 range.insertNode(this.doc.createTextNode(txt));
22300 * Executes a Midas editor command on the editor document and performs necessary focus and
22301 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22302 * @param {String} cmd The Midas command
22303 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22305 relayCmd : function(cmd, value){
22307 this.execCmd(cmd, value);
22308 this.owner.fireEvent('editorevent', this);
22309 //this.updateToolbar();
22310 this.owner.deferFocus();
22314 * Executes a Midas editor command directly on the editor document.
22315 * For visual commands, you should use {@link #relayCmd} instead.
22316 * <b>This should only be called after the editor is initialized.</b>
22317 * @param {String} cmd The Midas command
22318 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22320 execCmd : function(cmd, value){
22321 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22328 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22330 * @param {String} text | dom node..
22332 insertAtCursor : function(text)
22335 if(!this.activated){
22341 var r = this.doc.selection.createRange();
22352 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22356 // from jquery ui (MIT licenced)
22358 var win = this.win;
22360 if (win.getSelection && win.getSelection().getRangeAt) {
22361 range = win.getSelection().getRangeAt(0);
22362 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22363 range.insertNode(node);
22364 } else if (win.document.selection && win.document.selection.createRange) {
22365 // no firefox support
22366 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22367 win.document.selection.createRange().pasteHTML(txt);
22369 // no firefox support
22370 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22371 this.execCmd('InsertHTML', txt);
22380 mozKeyPress : function(e){
22382 var c = e.getCharCode(), cmd;
22385 c = String.fromCharCode(c).toLowerCase();
22399 this.cleanUpPaste.defer(100, this);
22407 e.preventDefault();
22415 fixKeys : function(){ // load time branching for fastest keydown performance
22417 return function(e){
22418 var k = e.getKey(), r;
22421 r = this.doc.selection.createRange();
22424 r.pasteHTML('    ');
22431 r = this.doc.selection.createRange();
22433 var target = r.parentElement();
22434 if(!target || target.tagName.toLowerCase() != 'li'){
22436 r.pasteHTML('<br />');
22442 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22443 this.cleanUpPaste.defer(100, this);
22449 }else if(Roo.isOpera){
22450 return function(e){
22451 var k = e.getKey();
22455 this.execCmd('InsertHTML','    ');
22458 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22459 this.cleanUpPaste.defer(100, this);
22464 }else if(Roo.isSafari){
22465 return function(e){
22466 var k = e.getKey();
22470 this.execCmd('InsertText','\t');
22474 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22475 this.cleanUpPaste.defer(100, this);
22483 getAllAncestors: function()
22485 var p = this.getSelectedNode();
22488 a.push(p); // push blank onto stack..
22489 p = this.getParentElement();
22493 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22497 a.push(this.doc.body);
22501 lastSelNode : false,
22504 getSelection : function()
22506 this.assignDocWin();
22507 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22510 getSelectedNode: function()
22512 // this may only work on Gecko!!!
22514 // should we cache this!!!!
22519 var range = this.createRange(this.getSelection()).cloneRange();
22522 var parent = range.parentElement();
22524 var testRange = range.duplicate();
22525 testRange.moveToElementText(parent);
22526 if (testRange.inRange(range)) {
22529 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22532 parent = parent.parentElement;
22537 // is ancestor a text element.
22538 var ac = range.commonAncestorContainer;
22539 if (ac.nodeType == 3) {
22540 ac = ac.parentNode;
22543 var ar = ac.childNodes;
22546 var other_nodes = [];
22547 var has_other_nodes = false;
22548 for (var i=0;i<ar.length;i++) {
22549 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22552 // fullly contained node.
22554 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22559 // probably selected..
22560 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22561 other_nodes.push(ar[i]);
22565 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22570 has_other_nodes = true;
22572 if (!nodes.length && other_nodes.length) {
22573 nodes= other_nodes;
22575 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22581 createRange: function(sel)
22583 // this has strange effects when using with
22584 // top toolbar - not sure if it's a great idea.
22585 //this.editor.contentWindow.focus();
22586 if (typeof sel != "undefined") {
22588 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22590 return this.doc.createRange();
22593 return this.doc.createRange();
22596 getParentElement: function()
22599 this.assignDocWin();
22600 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22602 var range = this.createRange(sel);
22605 var p = range.commonAncestorContainer;
22606 while (p.nodeType == 3) { // text node
22617 * Range intersection.. the hard stuff...
22621 * [ -- selected range --- ]
22625 * if end is before start or hits it. fail.
22626 * if start is after end or hits it fail.
22628 * if either hits (but other is outside. - then it's not
22634 // @see http://www.thismuchiknow.co.uk/?p=64.
22635 rangeIntersectsNode : function(range, node)
22637 var nodeRange = node.ownerDocument.createRange();
22639 nodeRange.selectNode(node);
22641 nodeRange.selectNodeContents(node);
22644 var rangeStartRange = range.cloneRange();
22645 rangeStartRange.collapse(true);
22647 var rangeEndRange = range.cloneRange();
22648 rangeEndRange.collapse(false);
22650 var nodeStartRange = nodeRange.cloneRange();
22651 nodeStartRange.collapse(true);
22653 var nodeEndRange = nodeRange.cloneRange();
22654 nodeEndRange.collapse(false);
22656 return rangeStartRange.compareBoundaryPoints(
22657 Range.START_TO_START, nodeEndRange) == -1 &&
22658 rangeEndRange.compareBoundaryPoints(
22659 Range.START_TO_START, nodeStartRange) == 1;
22663 rangeCompareNode : function(range, node)
22665 var nodeRange = node.ownerDocument.createRange();
22667 nodeRange.selectNode(node);
22669 nodeRange.selectNodeContents(node);
22673 range.collapse(true);
22675 nodeRange.collapse(true);
22677 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22678 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22680 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22682 var nodeIsBefore = ss == 1;
22683 var nodeIsAfter = ee == -1;
22685 if (nodeIsBefore && nodeIsAfter) {
22688 if (!nodeIsBefore && nodeIsAfter) {
22689 return 1; //right trailed.
22692 if (nodeIsBefore && !nodeIsAfter) {
22693 return 2; // left trailed.
22699 // private? - in a new class?
22700 cleanUpPaste : function()
22702 // cleans up the whole document..
22703 Roo.log('cleanuppaste');
22705 this.cleanUpChildren(this.doc.body);
22706 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22707 if (clean != this.doc.body.innerHTML) {
22708 this.doc.body.innerHTML = clean;
22713 cleanWordChars : function(input) {// change the chars to hex code
22714 var he = Roo.HtmlEditorCore;
22716 var output = input;
22717 Roo.each(he.swapCodes, function(sw) {
22718 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22720 output = output.replace(swapper, sw[1]);
22727 cleanUpChildren : function (n)
22729 if (!n.childNodes.length) {
22732 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22733 this.cleanUpChild(n.childNodes[i]);
22740 cleanUpChild : function (node)
22743 //console.log(node);
22744 if (node.nodeName == "#text") {
22745 // clean up silly Windows -- stuff?
22748 if (node.nodeName == "#comment") {
22749 node.parentNode.removeChild(node);
22750 // clean up silly Windows -- stuff?
22753 var lcname = node.tagName.toLowerCase();
22754 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22755 // whitelist of tags..
22757 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22759 node.parentNode.removeChild(node);
22764 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22766 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22767 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22769 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22770 // remove_keep_children = true;
22773 if (remove_keep_children) {
22774 this.cleanUpChildren(node);
22775 // inserts everything just before this node...
22776 while (node.childNodes.length) {
22777 var cn = node.childNodes[0];
22778 node.removeChild(cn);
22779 node.parentNode.insertBefore(cn, node);
22781 node.parentNode.removeChild(node);
22785 if (!node.attributes || !node.attributes.length) {
22786 this.cleanUpChildren(node);
22790 function cleanAttr(n,v)
22793 if (v.match(/^\./) || v.match(/^\//)) {
22796 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22799 if (v.match(/^#/)) {
22802 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22803 node.removeAttribute(n);
22807 var cwhite = this.cwhite;
22808 var cblack = this.cblack;
22810 function cleanStyle(n,v)
22812 if (v.match(/expression/)) { //XSS?? should we even bother..
22813 node.removeAttribute(n);
22817 var parts = v.split(/;/);
22820 Roo.each(parts, function(p) {
22821 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22825 var l = p.split(':').shift().replace(/\s+/g,'');
22826 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22828 if ( cwhite.length && cblack.indexOf(l) > -1) {
22829 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22830 //node.removeAttribute(n);
22834 // only allow 'c whitelisted system attributes'
22835 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22836 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22837 //node.removeAttribute(n);
22847 if (clean.length) {
22848 node.setAttribute(n, clean.join(';'));
22850 node.removeAttribute(n);
22856 for (var i = node.attributes.length-1; i > -1 ; i--) {
22857 var a = node.attributes[i];
22860 if (a.name.toLowerCase().substr(0,2)=='on') {
22861 node.removeAttribute(a.name);
22864 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22865 node.removeAttribute(a.name);
22868 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22869 cleanAttr(a.name,a.value); // fixme..
22872 if (a.name == 'style') {
22873 cleanStyle(a.name,a.value);
22876 /// clean up MS crap..
22877 // tecnically this should be a list of valid class'es..
22880 if (a.name == 'class') {
22881 if (a.value.match(/^Mso/)) {
22882 node.className = '';
22885 if (a.value.match(/^body$/)) {
22886 node.className = '';
22897 this.cleanUpChildren(node);
22903 * Clean up MS wordisms...
22905 cleanWord : function(node)
22910 this.cleanWord(this.doc.body);
22913 if (node.nodeName == "#text") {
22914 // clean up silly Windows -- stuff?
22917 if (node.nodeName == "#comment") {
22918 node.parentNode.removeChild(node);
22919 // clean up silly Windows -- stuff?
22923 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22924 node.parentNode.removeChild(node);
22928 // remove - but keep children..
22929 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22930 while (node.childNodes.length) {
22931 var cn = node.childNodes[0];
22932 node.removeChild(cn);
22933 node.parentNode.insertBefore(cn, node);
22935 node.parentNode.removeChild(node);
22936 this.iterateChildren(node, this.cleanWord);
22940 if (node.className.length) {
22942 var cn = node.className.split(/\W+/);
22944 Roo.each(cn, function(cls) {
22945 if (cls.match(/Mso[a-zA-Z]+/)) {
22950 node.className = cna.length ? cna.join(' ') : '';
22952 node.removeAttribute("class");
22956 if (node.hasAttribute("lang")) {
22957 node.removeAttribute("lang");
22960 if (node.hasAttribute("style")) {
22962 var styles = node.getAttribute("style").split(";");
22964 Roo.each(styles, function(s) {
22965 if (!s.match(/:/)) {
22968 var kv = s.split(":");
22969 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22972 // what ever is left... we allow.
22975 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22976 if (!nstyle.length) {
22977 node.removeAttribute('style');
22980 this.iterateChildren(node, this.cleanWord);
22986 * iterateChildren of a Node, calling fn each time, using this as the scole..
22987 * @param {DomNode} node node to iterate children of.
22988 * @param {Function} fn method of this class to call on each item.
22990 iterateChildren : function(node, fn)
22992 if (!node.childNodes.length) {
22995 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22996 fn.call(this, node.childNodes[i])
23002 * cleanTableWidths.
23004 * Quite often pasting from word etc.. results in tables with column and widths.
23005 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23008 cleanTableWidths : function(node)
23013 this.cleanTableWidths(this.doc.body);
23018 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23021 Roo.log(node.tagName);
23022 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23023 this.iterateChildren(node, this.cleanTableWidths);
23026 if (node.hasAttribute('width')) {
23027 node.removeAttribute('width');
23031 if (node.hasAttribute("style")) {
23034 var styles = node.getAttribute("style").split(";");
23036 Roo.each(styles, function(s) {
23037 if (!s.match(/:/)) {
23040 var kv = s.split(":");
23041 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23044 // what ever is left... we allow.
23047 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23048 if (!nstyle.length) {
23049 node.removeAttribute('style');
23053 this.iterateChildren(node, this.cleanTableWidths);
23061 domToHTML : function(currentElement, depth, nopadtext) {
23063 depth = depth || 0;
23064 nopadtext = nopadtext || false;
23066 if (!currentElement) {
23067 return this.domToHTML(this.doc.body);
23070 //Roo.log(currentElement);
23072 var allText = false;
23073 var nodeName = currentElement.nodeName;
23074 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23076 if (nodeName == '#text') {
23078 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23083 if (nodeName != 'BODY') {
23086 // Prints the node tagName, such as <A>, <IMG>, etc
23089 for(i = 0; i < currentElement.attributes.length;i++) {
23091 var aname = currentElement.attributes.item(i).name;
23092 if (!currentElement.attributes.item(i).value.length) {
23095 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23098 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23107 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23110 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23115 // Traverse the tree
23117 var currentElementChild = currentElement.childNodes.item(i);
23118 var allText = true;
23119 var innerHTML = '';
23121 while (currentElementChild) {
23122 // Formatting code (indent the tree so it looks nice on the screen)
23123 var nopad = nopadtext;
23124 if (lastnode == 'SPAN') {
23128 if (currentElementChild.nodeName == '#text') {
23129 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23130 toadd = nopadtext ? toadd : toadd.trim();
23131 if (!nopad && toadd.length > 80) {
23132 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23134 innerHTML += toadd;
23137 currentElementChild = currentElement.childNodes.item(i);
23143 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23145 // Recursively traverse the tree structure of the child node
23146 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23147 lastnode = currentElementChild.nodeName;
23149 currentElementChild=currentElement.childNodes.item(i);
23155 // The remaining code is mostly for formatting the tree
23156 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23161 ret+= "</"+tagName+">";
23167 applyBlacklists : function()
23169 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23170 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23174 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23175 if (b.indexOf(tag) > -1) {
23178 this.white.push(tag);
23182 Roo.each(w, function(tag) {
23183 if (b.indexOf(tag) > -1) {
23186 if (this.white.indexOf(tag) > -1) {
23189 this.white.push(tag);
23194 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23195 if (w.indexOf(tag) > -1) {
23198 this.black.push(tag);
23202 Roo.each(b, function(tag) {
23203 if (w.indexOf(tag) > -1) {
23206 if (this.black.indexOf(tag) > -1) {
23209 this.black.push(tag);
23214 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23215 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23219 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23220 if (b.indexOf(tag) > -1) {
23223 this.cwhite.push(tag);
23227 Roo.each(w, function(tag) {
23228 if (b.indexOf(tag) > -1) {
23231 if (this.cwhite.indexOf(tag) > -1) {
23234 this.cwhite.push(tag);
23239 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23240 if (w.indexOf(tag) > -1) {
23243 this.cblack.push(tag);
23247 Roo.each(b, function(tag) {
23248 if (w.indexOf(tag) > -1) {
23251 if (this.cblack.indexOf(tag) > -1) {
23254 this.cblack.push(tag);
23259 setStylesheets : function(stylesheets)
23261 if(typeof(stylesheets) == 'string'){
23262 Roo.get(this.iframe.contentDocument.head).createChild({
23264 rel : 'stylesheet',
23273 Roo.each(stylesheets, function(s) {
23278 Roo.get(_this.iframe.contentDocument.head).createChild({
23280 rel : 'stylesheet',
23289 removeStylesheets : function()
23293 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23298 setStyle : function(style)
23300 Roo.get(this.iframe.contentDocument.head).createChild({
23309 // hide stuff that is not compatible
23323 * @event specialkey
23327 * @cfg {String} fieldClass @hide
23330 * @cfg {String} focusClass @hide
23333 * @cfg {String} autoCreate @hide
23336 * @cfg {String} inputType @hide
23339 * @cfg {String} invalidClass @hide
23342 * @cfg {String} invalidText @hide
23345 * @cfg {String} msgFx @hide
23348 * @cfg {String} validateOnBlur @hide
23352 Roo.HtmlEditorCore.white = [
23353 'area', 'br', 'img', 'input', 'hr', 'wbr',
23355 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23356 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23357 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23358 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23359 'table', 'ul', 'xmp',
23361 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23364 'dir', 'menu', 'ol', 'ul', 'dl',
23370 Roo.HtmlEditorCore.black = [
23371 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23373 'base', 'basefont', 'bgsound', 'blink', 'body',
23374 'frame', 'frameset', 'head', 'html', 'ilayer',
23375 'iframe', 'layer', 'link', 'meta', 'object',
23376 'script', 'style' ,'title', 'xml' // clean later..
23378 Roo.HtmlEditorCore.clean = [
23379 'script', 'style', 'title', 'xml'
23381 Roo.HtmlEditorCore.remove = [
23386 Roo.HtmlEditorCore.ablack = [
23390 Roo.HtmlEditorCore.aclean = [
23391 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23395 Roo.HtmlEditorCore.pwhite= [
23396 'http', 'https', 'mailto'
23399 // white listed style attributes.
23400 Roo.HtmlEditorCore.cwhite= [
23401 // 'text-align', /// default is to allow most things..
23407 // black listed style attributes.
23408 Roo.HtmlEditorCore.cblack= [
23409 // 'font-size' -- this can be set by the project
23413 Roo.HtmlEditorCore.swapCodes =[
23432 * @class Roo.bootstrap.HtmlEditor
23433 * @extends Roo.bootstrap.TextArea
23434 * Bootstrap HtmlEditor class
23437 * Create a new HtmlEditor
23438 * @param {Object} config The config object
23441 Roo.bootstrap.HtmlEditor = function(config){
23442 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23443 if (!this.toolbars) {
23444 this.toolbars = [];
23447 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23450 * @event initialize
23451 * Fires when the editor is fully initialized (including the iframe)
23452 * @param {HtmlEditor} this
23457 * Fires when the editor is first receives the focus. Any insertion must wait
23458 * until after this event.
23459 * @param {HtmlEditor} this
23463 * @event beforesync
23464 * Fires before the textarea is updated with content from the editor iframe. Return false
23465 * to cancel the sync.
23466 * @param {HtmlEditor} this
23467 * @param {String} html
23471 * @event beforepush
23472 * Fires before the iframe editor is updated with content from the textarea. Return false
23473 * to cancel the push.
23474 * @param {HtmlEditor} this
23475 * @param {String} html
23480 * Fires when the textarea is updated with content from the editor iframe.
23481 * @param {HtmlEditor} this
23482 * @param {String} html
23487 * Fires when the iframe editor is updated with content from the textarea.
23488 * @param {HtmlEditor} this
23489 * @param {String} html
23493 * @event editmodechange
23494 * Fires when the editor switches edit modes
23495 * @param {HtmlEditor} this
23496 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23498 editmodechange: true,
23500 * @event editorevent
23501 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23502 * @param {HtmlEditor} this
23506 * @event firstfocus
23507 * Fires when on first focus - needed by toolbars..
23508 * @param {HtmlEditor} this
23513 * Auto save the htmlEditor value as a file into Events
23514 * @param {HtmlEditor} this
23518 * @event savedpreview
23519 * preview the saved version of htmlEditor
23520 * @param {HtmlEditor} this
23527 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23531 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23536 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23541 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23546 * @cfg {Number} height (in pixels)
23550 * @cfg {Number} width (in pixels)
23555 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23558 stylesheets: false,
23563 // private properties
23564 validationEvent : false,
23566 initialized : false,
23569 onFocus : Roo.emptyFn,
23571 hideMode:'offsets',
23573 tbContainer : false,
23577 toolbarContainer :function() {
23578 return this.wrap.select('.x-html-editor-tb',true).first();
23582 * Protected method that will not generally be called directly. It
23583 * is called when the editor creates its toolbar. Override this method if you need to
23584 * add custom toolbar buttons.
23585 * @param {HtmlEditor} editor
23587 createToolbar : function(){
23588 Roo.log('renewing');
23589 Roo.log("create toolbars");
23591 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23592 this.toolbars[0].render(this.toolbarContainer());
23596 // if (!editor.toolbars || !editor.toolbars.length) {
23597 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23600 // for (var i =0 ; i < editor.toolbars.length;i++) {
23601 // editor.toolbars[i] = Roo.factory(
23602 // typeof(editor.toolbars[i]) == 'string' ?
23603 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23604 // Roo.bootstrap.HtmlEditor);
23605 // editor.toolbars[i].init(editor);
23611 onRender : function(ct, position)
23613 // Roo.log("Call onRender: " + this.xtype);
23615 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23617 this.wrap = this.inputEl().wrap({
23618 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23621 this.editorcore.onRender(ct, position);
23623 if (this.resizable) {
23624 this.resizeEl = new Roo.Resizable(this.wrap, {
23628 minHeight : this.height,
23629 height: this.height,
23630 handles : this.resizable,
23633 resize : function(r, w, h) {
23634 _t.onResize(w,h); // -something
23640 this.createToolbar(this);
23643 if(!this.width && this.resizable){
23644 this.setSize(this.wrap.getSize());
23646 if (this.resizeEl) {
23647 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23648 // should trigger onReize..
23654 onResize : function(w, h)
23656 Roo.log('resize: ' +w + ',' + h );
23657 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23661 if(this.inputEl() ){
23662 if(typeof w == 'number'){
23663 var aw = w - this.wrap.getFrameWidth('lr');
23664 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23667 if(typeof h == 'number'){
23668 var tbh = -11; // fixme it needs to tool bar size!
23669 for (var i =0; i < this.toolbars.length;i++) {
23670 // fixme - ask toolbars for heights?
23671 tbh += this.toolbars[i].el.getHeight();
23672 //if (this.toolbars[i].footer) {
23673 // tbh += this.toolbars[i].footer.el.getHeight();
23681 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23682 ah -= 5; // knock a few pixes off for look..
23683 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23687 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23688 this.editorcore.onResize(ew,eh);
23693 * Toggles the editor between standard and source edit mode.
23694 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23696 toggleSourceEdit : function(sourceEditMode)
23698 this.editorcore.toggleSourceEdit(sourceEditMode);
23700 if(this.editorcore.sourceEditMode){
23701 Roo.log('editor - showing textarea');
23704 // Roo.log(this.syncValue());
23706 this.inputEl().removeClass(['hide', 'x-hidden']);
23707 this.inputEl().dom.removeAttribute('tabIndex');
23708 this.inputEl().focus();
23710 Roo.log('editor - hiding textarea');
23712 // Roo.log(this.pushValue());
23715 this.inputEl().addClass(['hide', 'x-hidden']);
23716 this.inputEl().dom.setAttribute('tabIndex', -1);
23717 //this.deferFocus();
23720 if(this.resizable){
23721 this.setSize(this.wrap.getSize());
23724 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23727 // private (for BoxComponent)
23728 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23730 // private (for BoxComponent)
23731 getResizeEl : function(){
23735 // private (for BoxComponent)
23736 getPositionEl : function(){
23741 initEvents : function(){
23742 this.originalValue = this.getValue();
23746 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23749 // markInvalid : Roo.emptyFn,
23751 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23754 // clearInvalid : Roo.emptyFn,
23756 setValue : function(v){
23757 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23758 this.editorcore.pushValue();
23763 deferFocus : function(){
23764 this.focus.defer(10, this);
23768 focus : function(){
23769 this.editorcore.focus();
23775 onDestroy : function(){
23781 for (var i =0; i < this.toolbars.length;i++) {
23782 // fixme - ask toolbars for heights?
23783 this.toolbars[i].onDestroy();
23786 this.wrap.dom.innerHTML = '';
23787 this.wrap.remove();
23792 onFirstFocus : function(){
23793 //Roo.log("onFirstFocus");
23794 this.editorcore.onFirstFocus();
23795 for (var i =0; i < this.toolbars.length;i++) {
23796 this.toolbars[i].onFirstFocus();
23802 syncValue : function()
23804 this.editorcore.syncValue();
23807 pushValue : function()
23809 this.editorcore.pushValue();
23813 // hide stuff that is not compatible
23827 * @event specialkey
23831 * @cfg {String} fieldClass @hide
23834 * @cfg {String} focusClass @hide
23837 * @cfg {String} autoCreate @hide
23840 * @cfg {String} inputType @hide
23843 * @cfg {String} invalidClass @hide
23846 * @cfg {String} invalidText @hide
23849 * @cfg {String} msgFx @hide
23852 * @cfg {String} validateOnBlur @hide
23861 Roo.namespace('Roo.bootstrap.htmleditor');
23863 * @class Roo.bootstrap.HtmlEditorToolbar1
23868 new Roo.bootstrap.HtmlEditor({
23871 new Roo.bootstrap.HtmlEditorToolbar1({
23872 disable : { fonts: 1 , format: 1, ..., ... , ...],
23878 * @cfg {Object} disable List of elements to disable..
23879 * @cfg {Array} btns List of additional buttons.
23883 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23886 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23889 Roo.apply(this, config);
23891 // default disabled, based on 'good practice'..
23892 this.disable = this.disable || {};
23893 Roo.applyIf(this.disable, {
23896 specialElements : true
23898 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23900 this.editor = config.editor;
23901 this.editorcore = config.editor.editorcore;
23903 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23905 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23906 // dont call parent... till later.
23908 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23913 editorcore : false,
23918 "h1","h2","h3","h4","h5","h6",
23920 "abbr", "acronym", "address", "cite", "samp", "var",
23924 onRender : function(ct, position)
23926 // Roo.log("Call onRender: " + this.xtype);
23928 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23930 this.el.dom.style.marginBottom = '0';
23932 var editorcore = this.editorcore;
23933 var editor= this.editor;
23936 var btn = function(id,cmd , toggle, handler, html){
23938 var event = toggle ? 'toggle' : 'click';
23943 xns: Roo.bootstrap,
23946 enableToggle:toggle !== false,
23948 pressed : toggle ? false : null,
23951 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23952 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23958 // var cb_box = function...
23963 xns: Roo.bootstrap,
23964 glyphicon : 'font',
23968 xns: Roo.bootstrap,
23972 Roo.each(this.formats, function(f) {
23973 style.menu.items.push({
23975 xns: Roo.bootstrap,
23976 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23981 editorcore.insertTag(this.tagname);
23988 children.push(style);
23990 btn('bold',false,true);
23991 btn('italic',false,true);
23992 btn('align-left', 'justifyleft',true);
23993 btn('align-center', 'justifycenter',true);
23994 btn('align-right' , 'justifyright',true);
23995 btn('link', false, false, function(btn) {
23996 //Roo.log("create link?");
23997 var url = prompt(this.createLinkText, this.defaultLinkValue);
23998 if(url && url != 'http:/'+'/'){
23999 this.editorcore.relayCmd('createlink', url);
24002 btn('list','insertunorderedlist',true);
24003 btn('pencil', false,true, function(btn){
24005 this.toggleSourceEdit(btn.pressed);
24008 if (this.editor.btns.length > 0) {
24009 for (var i = 0; i<this.editor.btns.length; i++) {
24010 children.push(this.editor.btns[i]);
24018 xns: Roo.bootstrap,
24023 xns: Roo.bootstrap,
24028 cog.menu.items.push({
24030 xns: Roo.bootstrap,
24031 html : Clean styles,
24036 editorcore.insertTag(this.tagname);
24045 this.xtype = 'NavSimplebar';
24047 for(var i=0;i< children.length;i++) {
24049 this.buttons.add(this.addxtypeChild(children[i]));
24053 editor.on('editorevent', this.updateToolbar, this);
24055 onBtnClick : function(id)
24057 this.editorcore.relayCmd(id);
24058 this.editorcore.focus();
24062 * Protected method that will not generally be called directly. It triggers
24063 * a toolbar update by reading the markup state of the current selection in the editor.
24065 updateToolbar: function(){
24067 if(!this.editorcore.activated){
24068 this.editor.onFirstFocus(); // is this neeed?
24072 var btns = this.buttons;
24073 var doc = this.editorcore.doc;
24074 btns.get('bold').setActive(doc.queryCommandState('bold'));
24075 btns.get('italic').setActive(doc.queryCommandState('italic'));
24076 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24078 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24079 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24080 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24082 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24083 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24086 var ans = this.editorcore.getAllAncestors();
24087 if (this.formatCombo) {
24090 var store = this.formatCombo.store;
24091 this.formatCombo.setValue("");
24092 for (var i =0; i < ans.length;i++) {
24093 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24095 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24103 // hides menus... - so this cant be on a menu...
24104 Roo.bootstrap.MenuMgr.hideAll();
24106 Roo.bootstrap.MenuMgr.hideAll();
24107 //this.editorsyncValue();
24109 onFirstFocus: function() {
24110 this.buttons.each(function(item){
24114 toggleSourceEdit : function(sourceEditMode){
24117 if(sourceEditMode){
24118 Roo.log("disabling buttons");
24119 this.buttons.each( function(item){
24120 if(item.cmd != 'pencil'){
24126 Roo.log("enabling buttons");
24127 if(this.editorcore.initialized){
24128 this.buttons.each( function(item){
24134 Roo.log("calling toggole on editor");
24135 // tell the editor that it's been pressed..
24136 this.editor.toggleSourceEdit(sourceEditMode);
24146 * @class Roo.bootstrap.Table.AbstractSelectionModel
24147 * @extends Roo.util.Observable
24148 * Abstract base class for grid SelectionModels. It provides the interface that should be
24149 * implemented by descendant classes. This class should not be directly instantiated.
24152 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24153 this.locked = false;
24154 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24158 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24159 /** @ignore Called by the grid automatically. Do not call directly. */
24160 init : function(grid){
24166 * Locks the selections.
24169 this.locked = true;
24173 * Unlocks the selections.
24175 unlock : function(){
24176 this.locked = false;
24180 * Returns true if the selections are locked.
24181 * @return {Boolean}
24183 isLocked : function(){
24184 return this.locked;
24188 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24189 * @class Roo.bootstrap.Table.RowSelectionModel
24190 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24191 * It supports multiple selections and keyboard selection/navigation.
24193 * @param {Object} config
24196 Roo.bootstrap.Table.RowSelectionModel = function(config){
24197 Roo.apply(this, config);
24198 this.selections = new Roo.util.MixedCollection(false, function(o){
24203 this.lastActive = false;
24207 * @event selectionchange
24208 * Fires when the selection changes
24209 * @param {SelectionModel} this
24211 "selectionchange" : true,
24213 * @event afterselectionchange
24214 * Fires after the selection changes (eg. by key press or clicking)
24215 * @param {SelectionModel} this
24217 "afterselectionchange" : true,
24219 * @event beforerowselect
24220 * Fires when a row is selected being selected, return false to cancel.
24221 * @param {SelectionModel} this
24222 * @param {Number} rowIndex The selected index
24223 * @param {Boolean} keepExisting False if other selections will be cleared
24225 "beforerowselect" : true,
24228 * Fires when a row is selected.
24229 * @param {SelectionModel} this
24230 * @param {Number} rowIndex The selected index
24231 * @param {Roo.data.Record} r The record
24233 "rowselect" : true,
24235 * @event rowdeselect
24236 * Fires when a row is deselected.
24237 * @param {SelectionModel} this
24238 * @param {Number} rowIndex The selected index
24240 "rowdeselect" : true
24242 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24243 this.locked = false;
24246 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24248 * @cfg {Boolean} singleSelect
24249 * True to allow selection of only one row at a time (defaults to false)
24251 singleSelect : false,
24254 initEvents : function()
24257 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24258 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24259 //}else{ // allow click to work like normal
24260 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24262 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24263 this.grid.on("rowclick", this.handleMouseDown, this);
24265 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24266 "up" : function(e){
24268 this.selectPrevious(e.shiftKey);
24269 }else if(this.last !== false && this.lastActive !== false){
24270 var last = this.last;
24271 this.selectRange(this.last, this.lastActive-1);
24272 this.grid.getView().focusRow(this.lastActive);
24273 if(last !== false){
24277 this.selectFirstRow();
24279 this.fireEvent("afterselectionchange", this);
24281 "down" : function(e){
24283 this.selectNext(e.shiftKey);
24284 }else if(this.last !== false && this.lastActive !== false){
24285 var last = this.last;
24286 this.selectRange(this.last, this.lastActive+1);
24287 this.grid.getView().focusRow(this.lastActive);
24288 if(last !== false){
24292 this.selectFirstRow();
24294 this.fireEvent("afterselectionchange", this);
24298 this.grid.store.on('load', function(){
24299 this.selections.clear();
24302 var view = this.grid.view;
24303 view.on("refresh", this.onRefresh, this);
24304 view.on("rowupdated", this.onRowUpdated, this);
24305 view.on("rowremoved", this.onRemove, this);
24310 onRefresh : function()
24312 var ds = this.grid.store, i, v = this.grid.view;
24313 var s = this.selections;
24314 s.each(function(r){
24315 if((i = ds.indexOfId(r.id)) != -1){
24324 onRemove : function(v, index, r){
24325 this.selections.remove(r);
24329 onRowUpdated : function(v, index, r){
24330 if(this.isSelected(r)){
24331 v.onRowSelect(index);
24337 * @param {Array} records The records to select
24338 * @param {Boolean} keepExisting (optional) True to keep existing selections
24340 selectRecords : function(records, keepExisting)
24343 this.clearSelections();
24345 var ds = this.grid.store;
24346 for(var i = 0, len = records.length; i < len; i++){
24347 this.selectRow(ds.indexOf(records[i]), true);
24352 * Gets the number of selected rows.
24355 getCount : function(){
24356 return this.selections.length;
24360 * Selects the first row in the grid.
24362 selectFirstRow : function(){
24367 * Select the last row.
24368 * @param {Boolean} keepExisting (optional) True to keep existing selections
24370 selectLastRow : function(keepExisting){
24371 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24372 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24376 * Selects the row immediately following the last selected row.
24377 * @param {Boolean} keepExisting (optional) True to keep existing selections
24379 selectNext : function(keepExisting)
24381 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24382 this.selectRow(this.last+1, keepExisting);
24383 this.grid.getView().focusRow(this.last);
24388 * Selects the row that precedes the last selected row.
24389 * @param {Boolean} keepExisting (optional) True to keep existing selections
24391 selectPrevious : function(keepExisting){
24393 this.selectRow(this.last-1, keepExisting);
24394 this.grid.getView().focusRow(this.last);
24399 * Returns the selected records
24400 * @return {Array} Array of selected records
24402 getSelections : function(){
24403 return [].concat(this.selections.items);
24407 * Returns the first selected record.
24410 getSelected : function(){
24411 return this.selections.itemAt(0);
24416 * Clears all selections.
24418 clearSelections : function(fast)
24424 var ds = this.grid.store;
24425 var s = this.selections;
24426 s.each(function(r){
24427 this.deselectRow(ds.indexOfId(r.id));
24431 this.selections.clear();
24438 * Selects all rows.
24440 selectAll : function(){
24444 this.selections.clear();
24445 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24446 this.selectRow(i, true);
24451 * Returns True if there is a selection.
24452 * @return {Boolean}
24454 hasSelection : function(){
24455 return this.selections.length > 0;
24459 * Returns True if the specified row is selected.
24460 * @param {Number/Record} record The record or index of the record to check
24461 * @return {Boolean}
24463 isSelected : function(index){
24464 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24465 return (r && this.selections.key(r.id) ? true : false);
24469 * Returns True if the specified record id is selected.
24470 * @param {String} id The id of record to check
24471 * @return {Boolean}
24473 isIdSelected : function(id){
24474 return (this.selections.key(id) ? true : false);
24479 handleMouseDBClick : function(e, t){
24483 handleMouseDown : function(e, t)
24485 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24486 if(this.isLocked() || rowIndex < 0 ){
24489 if(e.shiftKey && this.last !== false){
24490 var last = this.last;
24491 this.selectRange(last, rowIndex, e.ctrlKey);
24492 this.last = last; // reset the last
24496 var isSelected = this.isSelected(rowIndex);
24497 //Roo.log("select row:" + rowIndex);
24499 this.deselectRow(rowIndex);
24501 this.selectRow(rowIndex, true);
24505 if(e.button !== 0 && isSelected){
24506 alert('rowIndex 2: ' + rowIndex);
24507 view.focusRow(rowIndex);
24508 }else if(e.ctrlKey && isSelected){
24509 this.deselectRow(rowIndex);
24510 }else if(!isSelected){
24511 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24512 view.focusRow(rowIndex);
24516 this.fireEvent("afterselectionchange", this);
24519 handleDragableRowClick : function(grid, rowIndex, e)
24521 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24522 this.selectRow(rowIndex, false);
24523 grid.view.focusRow(rowIndex);
24524 this.fireEvent("afterselectionchange", this);
24529 * Selects multiple rows.
24530 * @param {Array} rows Array of the indexes of the row to select
24531 * @param {Boolean} keepExisting (optional) True to keep existing selections
24533 selectRows : function(rows, keepExisting){
24535 this.clearSelections();
24537 for(var i = 0, len = rows.length; i < len; i++){
24538 this.selectRow(rows[i], true);
24543 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24544 * @param {Number} startRow The index of the first row in the range
24545 * @param {Number} endRow The index of the last row in the range
24546 * @param {Boolean} keepExisting (optional) True to retain existing selections
24548 selectRange : function(startRow, endRow, keepExisting){
24553 this.clearSelections();
24555 if(startRow <= endRow){
24556 for(var i = startRow; i <= endRow; i++){
24557 this.selectRow(i, true);
24560 for(var i = startRow; i >= endRow; i--){
24561 this.selectRow(i, true);
24567 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24568 * @param {Number} startRow The index of the first row in the range
24569 * @param {Number} endRow The index of the last row in the range
24571 deselectRange : function(startRow, endRow, preventViewNotify){
24575 for(var i = startRow; i <= endRow; i++){
24576 this.deselectRow(i, preventViewNotify);
24582 * @param {Number} row The index of the row to select
24583 * @param {Boolean} keepExisting (optional) True to keep existing selections
24585 selectRow : function(index, keepExisting, preventViewNotify)
24587 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24590 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24591 if(!keepExisting || this.singleSelect){
24592 this.clearSelections();
24595 var r = this.grid.store.getAt(index);
24596 //console.log('selectRow - record id :' + r.id);
24598 this.selections.add(r);
24599 this.last = this.lastActive = index;
24600 if(!preventViewNotify){
24601 var proxy = new Roo.Element(
24602 this.grid.getRowDom(index)
24604 proxy.addClass('bg-info info');
24606 this.fireEvent("rowselect", this, index, r);
24607 this.fireEvent("selectionchange", this);
24613 * @param {Number} row The index of the row to deselect
24615 deselectRow : function(index, preventViewNotify)
24620 if(this.last == index){
24623 if(this.lastActive == index){
24624 this.lastActive = false;
24627 var r = this.grid.store.getAt(index);
24632 this.selections.remove(r);
24633 //.console.log('deselectRow - record id :' + r.id);
24634 if(!preventViewNotify){
24636 var proxy = new Roo.Element(
24637 this.grid.getRowDom(index)
24639 proxy.removeClass('bg-info info');
24641 this.fireEvent("rowdeselect", this, index);
24642 this.fireEvent("selectionchange", this);
24646 restoreLast : function(){
24648 this.last = this._last;
24653 acceptsNav : function(row, col, cm){
24654 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24658 onEditorKey : function(field, e){
24659 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24664 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24666 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24668 }else if(k == e.ENTER && !e.ctrlKey){
24672 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24674 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24676 }else if(k == e.ESC){
24680 g.startEditing(newCell[0], newCell[1]);
24686 * Ext JS Library 1.1.1
24687 * Copyright(c) 2006-2007, Ext JS, LLC.
24689 * Originally Released Under LGPL - original licence link has changed is not relivant.
24692 * <script type="text/javascript">
24696 * @class Roo.bootstrap.PagingToolbar
24697 * @extends Roo.bootstrap.NavSimplebar
24698 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24700 * Create a new PagingToolbar
24701 * @param {Object} config The config object
24702 * @param {Roo.data.Store} store
24704 Roo.bootstrap.PagingToolbar = function(config)
24706 // old args format still supported... - xtype is prefered..
24707 // created from xtype...
24709 this.ds = config.dataSource;
24711 if (config.store && !this.ds) {
24712 this.store= Roo.factory(config.store, Roo.data);
24713 this.ds = this.store;
24714 this.ds.xmodule = this.xmodule || false;
24717 this.toolbarItems = [];
24718 if (config.items) {
24719 this.toolbarItems = config.items;
24722 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24727 this.bind(this.ds);
24730 if (Roo.bootstrap.version == 4) {
24731 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24733 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24738 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24740 * @cfg {Roo.data.Store} dataSource
24741 * The underlying data store providing the paged data
24744 * @cfg {String/HTMLElement/Element} container
24745 * container The id or element that will contain the toolbar
24748 * @cfg {Boolean} displayInfo
24749 * True to display the displayMsg (defaults to false)
24752 * @cfg {Number} pageSize
24753 * The number of records to display per page (defaults to 20)
24757 * @cfg {String} displayMsg
24758 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24760 displayMsg : 'Displaying {0} - {1} of {2}',
24762 * @cfg {String} emptyMsg
24763 * The message to display when no records are found (defaults to "No data to display")
24765 emptyMsg : 'No data to display',
24767 * Customizable piece of the default paging text (defaults to "Page")
24770 beforePageText : "Page",
24772 * Customizable piece of the default paging text (defaults to "of %0")
24775 afterPageText : "of {0}",
24777 * Customizable piece of the default paging text (defaults to "First Page")
24780 firstText : "First Page",
24782 * Customizable piece of the default paging text (defaults to "Previous Page")
24785 prevText : "Previous Page",
24787 * Customizable piece of the default paging text (defaults to "Next Page")
24790 nextText : "Next Page",
24792 * Customizable piece of the default paging text (defaults to "Last Page")
24795 lastText : "Last Page",
24797 * Customizable piece of the default paging text (defaults to "Refresh")
24800 refreshText : "Refresh",
24804 onRender : function(ct, position)
24806 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24807 this.navgroup.parentId = this.id;
24808 this.navgroup.onRender(this.el, null);
24809 // add the buttons to the navgroup
24811 if(this.displayInfo){
24812 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24813 this.displayEl = this.el.select('.x-paging-info', true).first();
24814 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24815 // this.displayEl = navel.el.select('span',true).first();
24821 Roo.each(_this.buttons, function(e){ // this might need to use render????
24822 Roo.factory(e).render(_this.el);
24826 Roo.each(_this.toolbarItems, function(e) {
24827 _this.navgroup.addItem(e);
24831 this.first = this.navgroup.addItem({
24832 tooltip: this.firstText,
24833 cls: "prev btn-outline-secondary",
24834 html : ' <i class="fa fa-step-backward"></i>',
24836 preventDefault: true,
24837 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24840 this.prev = this.navgroup.addItem({
24841 tooltip: this.prevText,
24842 cls: "prev btn-outline-secondary",
24843 html : ' <i class="fa fa-backward"></i>',
24845 preventDefault: true,
24846 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24848 //this.addSeparator();
24851 var field = this.navgroup.addItem( {
24853 cls : 'x-paging-position btn-outline-secondary',
24855 html : this.beforePageText +
24856 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24857 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24860 this.field = field.el.select('input', true).first();
24861 this.field.on("keydown", this.onPagingKeydown, this);
24862 this.field.on("focus", function(){this.dom.select();});
24865 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24866 //this.field.setHeight(18);
24867 //this.addSeparator();
24868 this.next = this.navgroup.addItem({
24869 tooltip: this.nextText,
24870 cls: "next btn-outline-secondary",
24871 html : ' <i class="fa fa-forward"></i>',
24873 preventDefault: true,
24874 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24876 this.last = this.navgroup.addItem({
24877 tooltip: this.lastText,
24878 html : ' <i class="fa fa-step-forward"></i>',
24879 cls: "next btn-outline-secondary",
24881 preventDefault: true,
24882 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24884 //this.addSeparator();
24885 this.loading = this.navgroup.addItem({
24886 tooltip: this.refreshText,
24887 cls: "btn-outline-secondary",
24888 html : ' <i class="fa fa-refresh"></i>',
24889 preventDefault: true,
24890 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24896 updateInfo : function(){
24897 if(this.displayEl){
24898 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24899 var msg = count == 0 ?
24903 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24905 this.displayEl.update(msg);
24910 onLoad : function(ds, r, o)
24912 this.cursor = o.params.start ? o.params.start : 0;
24914 var d = this.getPageData(),
24919 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24920 this.field.dom.value = ap;
24921 this.first.setDisabled(ap == 1);
24922 this.prev.setDisabled(ap == 1);
24923 this.next.setDisabled(ap == ps);
24924 this.last.setDisabled(ap == ps);
24925 this.loading.enable();
24930 getPageData : function(){
24931 var total = this.ds.getTotalCount();
24934 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24935 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24940 onLoadError : function(){
24941 this.loading.enable();
24945 onPagingKeydown : function(e){
24946 var k = e.getKey();
24947 var d = this.getPageData();
24949 var v = this.field.dom.value, pageNum;
24950 if(!v || isNaN(pageNum = parseInt(v, 10))){
24951 this.field.dom.value = d.activePage;
24954 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24955 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24958 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))
24960 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24961 this.field.dom.value = pageNum;
24962 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24965 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24967 var v = this.field.dom.value, pageNum;
24968 var increment = (e.shiftKey) ? 10 : 1;
24969 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24972 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24973 this.field.dom.value = d.activePage;
24976 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24978 this.field.dom.value = parseInt(v, 10) + increment;
24979 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24980 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24987 beforeLoad : function(){
24989 this.loading.disable();
24994 onClick : function(which){
25003 ds.load({params:{start: 0, limit: this.pageSize}});
25006 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25009 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25012 var total = ds.getTotalCount();
25013 var extra = total % this.pageSize;
25014 var lastStart = extra ? (total - extra) : total-this.pageSize;
25015 ds.load({params:{start: lastStart, limit: this.pageSize}});
25018 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25024 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25025 * @param {Roo.data.Store} store The data store to unbind
25027 unbind : function(ds){
25028 ds.un("beforeload", this.beforeLoad, this);
25029 ds.un("load", this.onLoad, this);
25030 ds.un("loadexception", this.onLoadError, this);
25031 ds.un("remove", this.updateInfo, this);
25032 ds.un("add", this.updateInfo, this);
25033 this.ds = undefined;
25037 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25038 * @param {Roo.data.Store} store The data store to bind
25040 bind : function(ds){
25041 ds.on("beforeload", this.beforeLoad, this);
25042 ds.on("load", this.onLoad, this);
25043 ds.on("loadexception", this.onLoadError, this);
25044 ds.on("remove", this.updateInfo, this);
25045 ds.on("add", this.updateInfo, this);
25056 * @class Roo.bootstrap.MessageBar
25057 * @extends Roo.bootstrap.Component
25058 * Bootstrap MessageBar class
25059 * @cfg {String} html contents of the MessageBar
25060 * @cfg {String} weight (info | success | warning | danger) default info
25061 * @cfg {String} beforeClass insert the bar before the given class
25062 * @cfg {Boolean} closable (true | false) default false
25063 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25066 * Create a new Element
25067 * @param {Object} config The config object
25070 Roo.bootstrap.MessageBar = function(config){
25071 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25074 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25080 beforeClass: 'bootstrap-sticky-wrap',
25082 getAutoCreate : function(){
25086 cls: 'alert alert-dismissable alert-' + this.weight,
25091 html: this.html || ''
25097 cfg.cls += ' alert-messages-fixed';
25111 onRender : function(ct, position)
25113 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25116 var cfg = Roo.apply({}, this.getAutoCreate());
25120 cfg.cls += ' ' + this.cls;
25123 cfg.style = this.style;
25125 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25127 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25130 this.el.select('>button.close').on('click', this.hide, this);
25136 if (!this.rendered) {
25142 this.fireEvent('show', this);
25148 if (!this.rendered) {
25154 this.fireEvent('hide', this);
25157 update : function()
25159 // var e = this.el.dom.firstChild;
25161 // if(this.closable){
25162 // e = e.nextSibling;
25165 // e.data = this.html || '';
25167 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25183 * @class Roo.bootstrap.Graph
25184 * @extends Roo.bootstrap.Component
25185 * Bootstrap Graph class
25189 @cfg {String} graphtype bar | vbar | pie
25190 @cfg {number} g_x coodinator | centre x (pie)
25191 @cfg {number} g_y coodinator | centre y (pie)
25192 @cfg {number} g_r radius (pie)
25193 @cfg {number} g_height height of the chart (respected by all elements in the set)
25194 @cfg {number} g_width width of the chart (respected by all elements in the set)
25195 @cfg {Object} title The title of the chart
25198 -opts (object) options for the chart
25200 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25201 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25203 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.
25204 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25206 o stretch (boolean)
25208 -opts (object) options for the pie
25211 o startAngle (number)
25212 o endAngle (number)
25216 * Create a new Input
25217 * @param {Object} config The config object
25220 Roo.bootstrap.Graph = function(config){
25221 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25227 * The img click event for the img.
25228 * @param {Roo.EventObject} e
25234 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25245 //g_colors: this.colors,
25252 getAutoCreate : function(){
25263 onRender : function(ct,position){
25266 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25268 if (typeof(Raphael) == 'undefined') {
25269 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25273 this.raphael = Raphael(this.el.dom);
25275 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25276 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25277 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25278 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25280 r.text(160, 10, "Single Series Chart").attr(txtattr);
25281 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25282 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25283 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25285 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25286 r.barchart(330, 10, 300, 220, data1);
25287 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25288 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25291 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25292 // r.barchart(30, 30, 560, 250, xdata, {
25293 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25294 // axis : "0 0 1 1",
25295 // axisxlabels : xdata
25296 // //yvalues : cols,
25299 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25301 // this.load(null,xdata,{
25302 // axis : "0 0 1 1",
25303 // axisxlabels : xdata
25308 load : function(graphtype,xdata,opts)
25310 this.raphael.clear();
25312 graphtype = this.graphtype;
25317 var r = this.raphael,
25318 fin = function () {
25319 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25321 fout = function () {
25322 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25324 pfin = function() {
25325 this.sector.stop();
25326 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25329 this.label[0].stop();
25330 this.label[0].attr({ r: 7.5 });
25331 this.label[1].attr({ "font-weight": 800 });
25334 pfout = function() {
25335 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25338 this.label[0].animate({ r: 5 }, 500, "bounce");
25339 this.label[1].attr({ "font-weight": 400 });
25345 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25348 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25351 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25352 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25354 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25361 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25366 setTitle: function(o)
25371 initEvents: function() {
25374 this.el.on('click', this.onClick, this);
25378 onClick : function(e)
25380 Roo.log('img onclick');
25381 this.fireEvent('click', this, e);
25393 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25396 * @class Roo.bootstrap.dash.NumberBox
25397 * @extends Roo.bootstrap.Component
25398 * Bootstrap NumberBox class
25399 * @cfg {String} headline Box headline
25400 * @cfg {String} content Box content
25401 * @cfg {String} icon Box icon
25402 * @cfg {String} footer Footer text
25403 * @cfg {String} fhref Footer href
25406 * Create a new NumberBox
25407 * @param {Object} config The config object
25411 Roo.bootstrap.dash.NumberBox = function(config){
25412 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25416 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25425 getAutoCreate : function(){
25429 cls : 'small-box ',
25437 cls : 'roo-headline',
25438 html : this.headline
25442 cls : 'roo-content',
25443 html : this.content
25457 cls : 'ion ' + this.icon
25466 cls : 'small-box-footer',
25467 href : this.fhref || '#',
25471 cfg.cn.push(footer);
25478 onRender : function(ct,position){
25479 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25486 setHeadline: function (value)
25488 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25491 setFooter: function (value, href)
25493 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25496 this.el.select('a.small-box-footer',true).first().attr('href', href);
25501 setContent: function (value)
25503 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25506 initEvents: function()
25520 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25523 * @class Roo.bootstrap.dash.TabBox
25524 * @extends Roo.bootstrap.Component
25525 * Bootstrap TabBox class
25526 * @cfg {String} title Title of the TabBox
25527 * @cfg {String} icon Icon of the TabBox
25528 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25529 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25532 * Create a new TabBox
25533 * @param {Object} config The config object
25537 Roo.bootstrap.dash.TabBox = function(config){
25538 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25543 * When a pane is added
25544 * @param {Roo.bootstrap.dash.TabPane} pane
25548 * @event activatepane
25549 * When a pane is activated
25550 * @param {Roo.bootstrap.dash.TabPane} pane
25552 "activatepane" : true
25560 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25565 tabScrollable : false,
25567 getChildContainer : function()
25569 return this.el.select('.tab-content', true).first();
25572 getAutoCreate : function(){
25576 cls: 'pull-left header',
25584 cls: 'fa ' + this.icon
25590 cls: 'nav nav-tabs pull-right',
25596 if(this.tabScrollable){
25603 cls: 'nav nav-tabs pull-right',
25614 cls: 'nav-tabs-custom',
25619 cls: 'tab-content no-padding',
25627 initEvents : function()
25629 //Roo.log('add add pane handler');
25630 this.on('addpane', this.onAddPane, this);
25633 * Updates the box title
25634 * @param {String} html to set the title to.
25636 setTitle : function(value)
25638 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25640 onAddPane : function(pane)
25642 this.panes.push(pane);
25643 //Roo.log('addpane');
25645 // tabs are rendere left to right..
25646 if(!this.showtabs){
25650 var ctr = this.el.select('.nav-tabs', true).first();
25653 var existing = ctr.select('.nav-tab',true);
25654 var qty = existing.getCount();;
25657 var tab = ctr.createChild({
25659 cls : 'nav-tab' + (qty ? '' : ' active'),
25667 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25670 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25672 pane.el.addClass('active');
25677 onTabClick : function(ev,un,ob,pane)
25679 //Roo.log('tab - prev default');
25680 ev.preventDefault();
25683 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25684 pane.tab.addClass('active');
25685 //Roo.log(pane.title);
25686 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25687 // technically we should have a deactivate event.. but maybe add later.
25688 // and it should not de-activate the selected tab...
25689 this.fireEvent('activatepane', pane);
25690 pane.el.addClass('active');
25691 pane.fireEvent('activate');
25696 getActivePane : function()
25699 Roo.each(this.panes, function(p) {
25700 if(p.el.hasClass('active')){
25721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25723 * @class Roo.bootstrap.TabPane
25724 * @extends Roo.bootstrap.Component
25725 * Bootstrap TabPane class
25726 * @cfg {Boolean} active (false | true) Default false
25727 * @cfg {String} title title of panel
25731 * Create a new TabPane
25732 * @param {Object} config The config object
25735 Roo.bootstrap.dash.TabPane = function(config){
25736 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25742 * When a pane is activated
25743 * @param {Roo.bootstrap.dash.TabPane} pane
25750 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25755 // the tabBox that this is attached to.
25758 getAutoCreate : function()
25766 cfg.cls += ' active';
25771 initEvents : function()
25773 //Roo.log('trigger add pane handler');
25774 this.parent().fireEvent('addpane', this)
25778 * Updates the tab title
25779 * @param {String} html to set the title to.
25781 setTitle: function(str)
25787 this.tab.select('a', true).first().dom.innerHTML = str;
25804 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25807 * @class Roo.bootstrap.menu.Menu
25808 * @extends Roo.bootstrap.Component
25809 * Bootstrap Menu class - container for Menu
25810 * @cfg {String} html Text of the menu
25811 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25812 * @cfg {String} icon Font awesome icon
25813 * @cfg {String} pos Menu align to (top | bottom) default bottom
25817 * Create a new Menu
25818 * @param {Object} config The config object
25822 Roo.bootstrap.menu.Menu = function(config){
25823 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25827 * @event beforeshow
25828 * Fires before this menu is displayed
25829 * @param {Roo.bootstrap.menu.Menu} this
25833 * @event beforehide
25834 * Fires before this menu is hidden
25835 * @param {Roo.bootstrap.menu.Menu} this
25840 * Fires after this menu is displayed
25841 * @param {Roo.bootstrap.menu.Menu} this
25846 * Fires after this menu is hidden
25847 * @param {Roo.bootstrap.menu.Menu} this
25852 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25853 * @param {Roo.bootstrap.menu.Menu} this
25854 * @param {Roo.EventObject} e
25861 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25865 weight : 'default',
25870 getChildContainer : function() {
25871 if(this.isSubMenu){
25875 return this.el.select('ul.dropdown-menu', true).first();
25878 getAutoCreate : function()
25883 cls : 'roo-menu-text',
25891 cls : 'fa ' + this.icon
25902 cls : 'dropdown-button btn btn-' + this.weight,
25907 cls : 'dropdown-toggle btn btn-' + this.weight,
25917 cls : 'dropdown-menu'
25923 if(this.pos == 'top'){
25924 cfg.cls += ' dropup';
25927 if(this.isSubMenu){
25930 cls : 'dropdown-menu'
25937 onRender : function(ct, position)
25939 this.isSubMenu = ct.hasClass('dropdown-submenu');
25941 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25944 initEvents : function()
25946 if(this.isSubMenu){
25950 this.hidden = true;
25952 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25953 this.triggerEl.on('click', this.onTriggerPress, this);
25955 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25956 this.buttonEl.on('click', this.onClick, this);
25962 if(this.isSubMenu){
25966 return this.el.select('ul.dropdown-menu', true).first();
25969 onClick : function(e)
25971 this.fireEvent("click", this, e);
25974 onTriggerPress : function(e)
25976 if (this.isVisible()) {
25983 isVisible : function(){
25984 return !this.hidden;
25989 this.fireEvent("beforeshow", this);
25991 this.hidden = false;
25992 this.el.addClass('open');
25994 Roo.get(document).on("mouseup", this.onMouseUp, this);
25996 this.fireEvent("show", this);
26003 this.fireEvent("beforehide", this);
26005 this.hidden = true;
26006 this.el.removeClass('open');
26008 Roo.get(document).un("mouseup", this.onMouseUp);
26010 this.fireEvent("hide", this);
26013 onMouseUp : function()
26027 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26030 * @class Roo.bootstrap.menu.Item
26031 * @extends Roo.bootstrap.Component
26032 * Bootstrap MenuItem class
26033 * @cfg {Boolean} submenu (true | false) default false
26034 * @cfg {String} html text of the item
26035 * @cfg {String} href the link
26036 * @cfg {Boolean} disable (true | false) default false
26037 * @cfg {Boolean} preventDefault (true | false) default true
26038 * @cfg {String} icon Font awesome icon
26039 * @cfg {String} pos Submenu align to (left | right) default right
26043 * Create a new Item
26044 * @param {Object} config The config object
26048 Roo.bootstrap.menu.Item = function(config){
26049 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26053 * Fires when the mouse is hovering over this menu
26054 * @param {Roo.bootstrap.menu.Item} this
26055 * @param {Roo.EventObject} e
26060 * Fires when the mouse exits this menu
26061 * @param {Roo.bootstrap.menu.Item} this
26062 * @param {Roo.EventObject} e
26068 * The raw click event for the entire grid.
26069 * @param {Roo.EventObject} e
26075 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26080 preventDefault: true,
26085 getAutoCreate : function()
26090 cls : 'roo-menu-item-text',
26098 cls : 'fa ' + this.icon
26107 href : this.href || '#',
26114 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26120 if(this.pos == 'left'){
26121 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26128 initEvents : function()
26130 this.el.on('mouseover', this.onMouseOver, this);
26131 this.el.on('mouseout', this.onMouseOut, this);
26133 this.el.select('a', true).first().on('click', this.onClick, this);
26137 onClick : function(e)
26139 if(this.preventDefault){
26140 e.preventDefault();
26143 this.fireEvent("click", this, e);
26146 onMouseOver : function(e)
26148 if(this.submenu && this.pos == 'left'){
26149 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26152 this.fireEvent("mouseover", this, e);
26155 onMouseOut : function(e)
26157 this.fireEvent("mouseout", this, e);
26169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26172 * @class Roo.bootstrap.menu.Separator
26173 * @extends Roo.bootstrap.Component
26174 * Bootstrap Separator class
26177 * Create a new Separator
26178 * @param {Object} config The config object
26182 Roo.bootstrap.menu.Separator = function(config){
26183 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26186 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26188 getAutoCreate : function(){
26209 * @class Roo.bootstrap.Tooltip
26210 * Bootstrap Tooltip class
26211 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26212 * to determine which dom element triggers the tooltip.
26214 * It needs to add support for additional attributes like tooltip-position
26217 * Create a new Toolti
26218 * @param {Object} config The config object
26221 Roo.bootstrap.Tooltip = function(config){
26222 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26224 this.alignment = Roo.bootstrap.Tooltip.alignment;
26226 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26227 this.alignment = config.alignment;
26232 Roo.apply(Roo.bootstrap.Tooltip, {
26234 * @function init initialize tooltip monitoring.
26238 currentTip : false,
26239 currentRegion : false,
26245 Roo.get(document).on('mouseover', this.enter ,this);
26246 Roo.get(document).on('mouseout', this.leave, this);
26249 this.currentTip = new Roo.bootstrap.Tooltip();
26252 enter : function(ev)
26254 var dom = ev.getTarget();
26256 //Roo.log(['enter',dom]);
26257 var el = Roo.fly(dom);
26258 if (this.currentEl) {
26260 //Roo.log(this.currentEl);
26261 //Roo.log(this.currentEl.contains(dom));
26262 if (this.currentEl == el) {
26265 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26271 if (this.currentTip.el) {
26272 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26276 if(!el || el.dom == document){
26282 // you can not look for children, as if el is the body.. then everythign is the child..
26283 if (!el.attr('tooltip')) { //
26284 if (!el.select("[tooltip]").elements.length) {
26287 // is the mouse over this child...?
26288 bindEl = el.select("[tooltip]").first();
26289 var xy = ev.getXY();
26290 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26291 //Roo.log("not in region.");
26294 //Roo.log("child element over..");
26297 this.currentEl = bindEl;
26298 this.currentTip.bind(bindEl);
26299 this.currentRegion = Roo.lib.Region.getRegion(dom);
26300 this.currentTip.enter();
26303 leave : function(ev)
26305 var dom = ev.getTarget();
26306 //Roo.log(['leave',dom]);
26307 if (!this.currentEl) {
26312 if (dom != this.currentEl.dom) {
26315 var xy = ev.getXY();
26316 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26319 // only activate leave if mouse cursor is outside... bounding box..
26324 if (this.currentTip) {
26325 this.currentTip.leave();
26327 //Roo.log('clear currentEl');
26328 this.currentEl = false;
26333 'left' : ['r-l', [-2,0], 'right'],
26334 'right' : ['l-r', [2,0], 'left'],
26335 'bottom' : ['t-b', [0,2], 'top'],
26336 'top' : [ 'b-t', [0,-2], 'bottom']
26342 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26347 delay : null, // can be { show : 300 , hide: 500}
26351 hoverState : null, //???
26353 placement : 'bottom',
26357 getAutoCreate : function(){
26364 cls : 'tooltip-arrow'
26367 cls : 'tooltip-inner'
26374 bind : function(el)
26380 enter : function () {
26382 if (this.timeout != null) {
26383 clearTimeout(this.timeout);
26386 this.hoverState = 'in';
26387 //Roo.log("enter - show");
26388 if (!this.delay || !this.delay.show) {
26393 this.timeout = setTimeout(function () {
26394 if (_t.hoverState == 'in') {
26397 }, this.delay.show);
26401 clearTimeout(this.timeout);
26403 this.hoverState = 'out';
26404 if (!this.delay || !this.delay.hide) {
26410 this.timeout = setTimeout(function () {
26411 //Roo.log("leave - timeout");
26413 if (_t.hoverState == 'out') {
26415 Roo.bootstrap.Tooltip.currentEl = false;
26420 show : function (msg)
26423 this.render(document.body);
26426 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26428 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26430 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26432 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26434 var placement = typeof this.placement == 'function' ?
26435 this.placement.call(this, this.el, on_el) :
26438 var autoToken = /\s?auto?\s?/i;
26439 var autoPlace = autoToken.test(placement);
26441 placement = placement.replace(autoToken, '') || 'top';
26445 //this.el.setXY([0,0]);
26447 //this.el.dom.style.display='block';
26449 //this.el.appendTo(on_el);
26451 var p = this.getPosition();
26452 var box = this.el.getBox();
26458 var align = this.alignment[placement];
26460 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26462 if(placement == 'top' || placement == 'bottom'){
26464 placement = 'right';
26467 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26468 placement = 'left';
26471 var scroll = Roo.select('body', true).first().getScroll();
26473 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26477 align = this.alignment[placement];
26480 this.el.alignTo(this.bindEl, align[0],align[1]);
26481 //var arrow = this.el.select('.arrow',true).first();
26482 //arrow.set(align[2],
26484 this.el.addClass(placement);
26486 this.el.addClass('in fade');
26488 this.hoverState = null;
26490 if (this.el.hasClass('fade')) {
26501 //this.el.setXY([0,0]);
26502 this.el.removeClass('in');
26518 * @class Roo.bootstrap.LocationPicker
26519 * @extends Roo.bootstrap.Component
26520 * Bootstrap LocationPicker class
26521 * @cfg {Number} latitude Position when init default 0
26522 * @cfg {Number} longitude Position when init default 0
26523 * @cfg {Number} zoom default 15
26524 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26525 * @cfg {Boolean} mapTypeControl default false
26526 * @cfg {Boolean} disableDoubleClickZoom default false
26527 * @cfg {Boolean} scrollwheel default true
26528 * @cfg {Boolean} streetViewControl default false
26529 * @cfg {Number} radius default 0
26530 * @cfg {String} locationName
26531 * @cfg {Boolean} draggable default true
26532 * @cfg {Boolean} enableAutocomplete default false
26533 * @cfg {Boolean} enableReverseGeocode default true
26534 * @cfg {String} markerTitle
26537 * Create a new LocationPicker
26538 * @param {Object} config The config object
26542 Roo.bootstrap.LocationPicker = function(config){
26544 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26549 * Fires when the picker initialized.
26550 * @param {Roo.bootstrap.LocationPicker} this
26551 * @param {Google Location} location
26555 * @event positionchanged
26556 * Fires when the picker position changed.
26557 * @param {Roo.bootstrap.LocationPicker} this
26558 * @param {Google Location} location
26560 positionchanged : true,
26563 * Fires when the map resize.
26564 * @param {Roo.bootstrap.LocationPicker} this
26569 * Fires when the map show.
26570 * @param {Roo.bootstrap.LocationPicker} this
26575 * Fires when the map hide.
26576 * @param {Roo.bootstrap.LocationPicker} this
26581 * Fires when click the map.
26582 * @param {Roo.bootstrap.LocationPicker} this
26583 * @param {Map event} e
26587 * @event mapRightClick
26588 * Fires when right click the map.
26589 * @param {Roo.bootstrap.LocationPicker} this
26590 * @param {Map event} e
26592 mapRightClick : true,
26594 * @event markerClick
26595 * Fires when click the marker.
26596 * @param {Roo.bootstrap.LocationPicker} this
26597 * @param {Map event} e
26599 markerClick : true,
26601 * @event markerRightClick
26602 * Fires when right click the marker.
26603 * @param {Roo.bootstrap.LocationPicker} this
26604 * @param {Map event} e
26606 markerRightClick : true,
26608 * @event OverlayViewDraw
26609 * Fires when OverlayView Draw
26610 * @param {Roo.bootstrap.LocationPicker} this
26612 OverlayViewDraw : true,
26614 * @event OverlayViewOnAdd
26615 * Fires when OverlayView Draw
26616 * @param {Roo.bootstrap.LocationPicker} this
26618 OverlayViewOnAdd : true,
26620 * @event OverlayViewOnRemove
26621 * Fires when OverlayView Draw
26622 * @param {Roo.bootstrap.LocationPicker} this
26624 OverlayViewOnRemove : true,
26626 * @event OverlayViewShow
26627 * Fires when OverlayView Draw
26628 * @param {Roo.bootstrap.LocationPicker} this
26629 * @param {Pixel} cpx
26631 OverlayViewShow : true,
26633 * @event OverlayViewHide
26634 * Fires when OverlayView Draw
26635 * @param {Roo.bootstrap.LocationPicker} this
26637 OverlayViewHide : true,
26639 * @event loadexception
26640 * Fires when load google lib failed.
26641 * @param {Roo.bootstrap.LocationPicker} this
26643 loadexception : true
26648 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26650 gMapContext: false,
26656 mapTypeControl: false,
26657 disableDoubleClickZoom: false,
26659 streetViewControl: false,
26663 enableAutocomplete: false,
26664 enableReverseGeocode: true,
26667 getAutoCreate: function()
26672 cls: 'roo-location-picker'
26678 initEvents: function(ct, position)
26680 if(!this.el.getWidth() || this.isApplied()){
26684 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26689 initial: function()
26691 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26692 this.fireEvent('loadexception', this);
26696 if(!this.mapTypeId){
26697 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26700 this.gMapContext = this.GMapContext();
26702 this.initOverlayView();
26704 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26708 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26709 _this.setPosition(_this.gMapContext.marker.position);
26712 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26713 _this.fireEvent('mapClick', this, event);
26717 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26718 _this.fireEvent('mapRightClick', this, event);
26722 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26723 _this.fireEvent('markerClick', this, event);
26727 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26728 _this.fireEvent('markerRightClick', this, event);
26732 this.setPosition(this.gMapContext.location);
26734 this.fireEvent('initial', this, this.gMapContext.location);
26737 initOverlayView: function()
26741 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26745 _this.fireEvent('OverlayViewDraw', _this);
26750 _this.fireEvent('OverlayViewOnAdd', _this);
26753 onRemove: function()
26755 _this.fireEvent('OverlayViewOnRemove', _this);
26758 show: function(cpx)
26760 _this.fireEvent('OverlayViewShow', _this, cpx);
26765 _this.fireEvent('OverlayViewHide', _this);
26771 fromLatLngToContainerPixel: function(event)
26773 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26776 isApplied: function()
26778 return this.getGmapContext() == false ? false : true;
26781 getGmapContext: function()
26783 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26786 GMapContext: function()
26788 var position = new google.maps.LatLng(this.latitude, this.longitude);
26790 var _map = new google.maps.Map(this.el.dom, {
26793 mapTypeId: this.mapTypeId,
26794 mapTypeControl: this.mapTypeControl,
26795 disableDoubleClickZoom: this.disableDoubleClickZoom,
26796 scrollwheel: this.scrollwheel,
26797 streetViewControl: this.streetViewControl,
26798 locationName: this.locationName,
26799 draggable: this.draggable,
26800 enableAutocomplete: this.enableAutocomplete,
26801 enableReverseGeocode: this.enableReverseGeocode
26804 var _marker = new google.maps.Marker({
26805 position: position,
26807 title: this.markerTitle,
26808 draggable: this.draggable
26815 location: position,
26816 radius: this.radius,
26817 locationName: this.locationName,
26818 addressComponents: {
26819 formatted_address: null,
26820 addressLine1: null,
26821 addressLine2: null,
26823 streetNumber: null,
26827 stateOrProvince: null
26830 domContainer: this.el.dom,
26831 geodecoder: new google.maps.Geocoder()
26835 drawCircle: function(center, radius, options)
26837 if (this.gMapContext.circle != null) {
26838 this.gMapContext.circle.setMap(null);
26842 options = Roo.apply({}, options, {
26843 strokeColor: "#0000FF",
26844 strokeOpacity: .35,
26846 fillColor: "#0000FF",
26850 options.map = this.gMapContext.map;
26851 options.radius = radius;
26852 options.center = center;
26853 this.gMapContext.circle = new google.maps.Circle(options);
26854 return this.gMapContext.circle;
26860 setPosition: function(location)
26862 this.gMapContext.location = location;
26863 this.gMapContext.marker.setPosition(location);
26864 this.gMapContext.map.panTo(location);
26865 this.drawCircle(location, this.gMapContext.radius, {});
26869 if (this.gMapContext.settings.enableReverseGeocode) {
26870 this.gMapContext.geodecoder.geocode({
26871 latLng: this.gMapContext.location
26872 }, function(results, status) {
26874 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26875 _this.gMapContext.locationName = results[0].formatted_address;
26876 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26878 _this.fireEvent('positionchanged', this, location);
26885 this.fireEvent('positionchanged', this, location);
26890 google.maps.event.trigger(this.gMapContext.map, "resize");
26892 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26894 this.fireEvent('resize', this);
26897 setPositionByLatLng: function(latitude, longitude)
26899 this.setPosition(new google.maps.LatLng(latitude, longitude));
26902 getCurrentPosition: function()
26905 latitude: this.gMapContext.location.lat(),
26906 longitude: this.gMapContext.location.lng()
26910 getAddressName: function()
26912 return this.gMapContext.locationName;
26915 getAddressComponents: function()
26917 return this.gMapContext.addressComponents;
26920 address_component_from_google_geocode: function(address_components)
26924 for (var i = 0; i < address_components.length; i++) {
26925 var component = address_components[i];
26926 if (component.types.indexOf("postal_code") >= 0) {
26927 result.postalCode = component.short_name;
26928 } else if (component.types.indexOf("street_number") >= 0) {
26929 result.streetNumber = component.short_name;
26930 } else if (component.types.indexOf("route") >= 0) {
26931 result.streetName = component.short_name;
26932 } else if (component.types.indexOf("neighborhood") >= 0) {
26933 result.city = component.short_name;
26934 } else if (component.types.indexOf("locality") >= 0) {
26935 result.city = component.short_name;
26936 } else if (component.types.indexOf("sublocality") >= 0) {
26937 result.district = component.short_name;
26938 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26939 result.stateOrProvince = component.short_name;
26940 } else if (component.types.indexOf("country") >= 0) {
26941 result.country = component.short_name;
26945 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26946 result.addressLine2 = "";
26950 setZoomLevel: function(zoom)
26952 this.gMapContext.map.setZoom(zoom);
26965 this.fireEvent('show', this);
26976 this.fireEvent('hide', this);
26981 Roo.apply(Roo.bootstrap.LocationPicker, {
26983 OverlayView : function(map, options)
26985 options = options || {};
26999 * @class Roo.bootstrap.Alert
27000 * @extends Roo.bootstrap.Component
27001 * Bootstrap Alert class
27002 * @cfg {String} title The title of alert
27003 * @cfg {String} html The content of alert
27004 * @cfg {String} weight ( success | info | warning | danger )
27005 * @cfg {String} faicon font-awesomeicon
27008 * Create a new alert
27009 * @param {Object} config The config object
27013 Roo.bootstrap.Alert = function(config){
27014 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27018 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27025 getAutoCreate : function()
27034 cls : 'roo-alert-icon'
27039 cls : 'roo-alert-title',
27044 cls : 'roo-alert-text',
27051 cfg.cn[0].cls += ' fa ' + this.faicon;
27055 cfg.cls += ' alert-' + this.weight;
27061 initEvents: function()
27063 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27066 setTitle : function(str)
27068 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27071 setText : function(str)
27073 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27076 setWeight : function(weight)
27079 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27082 this.weight = weight;
27084 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27087 setIcon : function(icon)
27090 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27093 this.faicon = icon;
27095 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27116 * @class Roo.bootstrap.UploadCropbox
27117 * @extends Roo.bootstrap.Component
27118 * Bootstrap UploadCropbox class
27119 * @cfg {String} emptyText show when image has been loaded
27120 * @cfg {String} rotateNotify show when image too small to rotate
27121 * @cfg {Number} errorTimeout default 3000
27122 * @cfg {Number} minWidth default 300
27123 * @cfg {Number} minHeight default 300
27124 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27125 * @cfg {Boolean} isDocument (true|false) default false
27126 * @cfg {String} url action url
27127 * @cfg {String} paramName default 'imageUpload'
27128 * @cfg {String} method default POST
27129 * @cfg {Boolean} loadMask (true|false) default true
27130 * @cfg {Boolean} loadingText default 'Loading...'
27133 * Create a new UploadCropbox
27134 * @param {Object} config The config object
27137 Roo.bootstrap.UploadCropbox = function(config){
27138 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27142 * @event beforeselectfile
27143 * Fire before select file
27144 * @param {Roo.bootstrap.UploadCropbox} this
27146 "beforeselectfile" : true,
27149 * Fire after initEvent
27150 * @param {Roo.bootstrap.UploadCropbox} this
27155 * Fire after initEvent
27156 * @param {Roo.bootstrap.UploadCropbox} this
27157 * @param {String} data
27162 * Fire when preparing the file data
27163 * @param {Roo.bootstrap.UploadCropbox} this
27164 * @param {Object} file
27169 * Fire when get exception
27170 * @param {Roo.bootstrap.UploadCropbox} this
27171 * @param {XMLHttpRequest} xhr
27173 "exception" : true,
27175 * @event beforeloadcanvas
27176 * Fire before load the canvas
27177 * @param {Roo.bootstrap.UploadCropbox} this
27178 * @param {String} src
27180 "beforeloadcanvas" : true,
27183 * Fire when trash image
27184 * @param {Roo.bootstrap.UploadCropbox} this
27189 * Fire when download the image
27190 * @param {Roo.bootstrap.UploadCropbox} this
27194 * @event footerbuttonclick
27195 * Fire when footerbuttonclick
27196 * @param {Roo.bootstrap.UploadCropbox} this
27197 * @param {String} type
27199 "footerbuttonclick" : true,
27203 * @param {Roo.bootstrap.UploadCropbox} this
27208 * Fire when rotate the image
27209 * @param {Roo.bootstrap.UploadCropbox} this
27210 * @param {String} pos
27215 * Fire when inspect the file
27216 * @param {Roo.bootstrap.UploadCropbox} this
27217 * @param {Object} file
27222 * Fire when xhr upload the file
27223 * @param {Roo.bootstrap.UploadCropbox} this
27224 * @param {Object} data
27229 * Fire when arrange the file data
27230 * @param {Roo.bootstrap.UploadCropbox} this
27231 * @param {Object} formData
27236 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27239 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27241 emptyText : 'Click to upload image',
27242 rotateNotify : 'Image is too small to rotate',
27243 errorTimeout : 3000,
27257 cropType : 'image/jpeg',
27259 canvasLoaded : false,
27260 isDocument : false,
27262 paramName : 'imageUpload',
27264 loadingText : 'Loading...',
27267 getAutoCreate : function()
27271 cls : 'roo-upload-cropbox',
27275 cls : 'roo-upload-cropbox-selector',
27280 cls : 'roo-upload-cropbox-body',
27281 style : 'cursor:pointer',
27285 cls : 'roo-upload-cropbox-preview'
27289 cls : 'roo-upload-cropbox-thumb'
27293 cls : 'roo-upload-cropbox-empty-notify',
27294 html : this.emptyText
27298 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27299 html : this.rotateNotify
27305 cls : 'roo-upload-cropbox-footer',
27308 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27318 onRender : function(ct, position)
27320 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27322 if (this.buttons.length) {
27324 Roo.each(this.buttons, function(bb) {
27326 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27328 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27334 this.maskEl = this.el;
27338 initEvents : function()
27340 this.urlAPI = (window.createObjectURL && window) ||
27341 (window.URL && URL.revokeObjectURL && URL) ||
27342 (window.webkitURL && webkitURL);
27344 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27345 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27348 this.selectorEl.hide();
27350 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27351 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27353 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27354 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27355 this.thumbEl.hide();
27357 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27358 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27360 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27361 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27362 this.errorEl.hide();
27364 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27365 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27366 this.footerEl.hide();
27368 this.setThumbBoxSize();
27374 this.fireEvent('initial', this);
27381 window.addEventListener("resize", function() { _this.resize(); } );
27383 this.bodyEl.on('click', this.beforeSelectFile, this);
27386 this.bodyEl.on('touchstart', this.onTouchStart, this);
27387 this.bodyEl.on('touchmove', this.onTouchMove, this);
27388 this.bodyEl.on('touchend', this.onTouchEnd, this);
27392 this.bodyEl.on('mousedown', this.onMouseDown, this);
27393 this.bodyEl.on('mousemove', this.onMouseMove, this);
27394 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27395 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27396 Roo.get(document).on('mouseup', this.onMouseUp, this);
27399 this.selectorEl.on('change', this.onFileSelected, this);
27405 this.baseScale = 1;
27407 this.baseRotate = 1;
27408 this.dragable = false;
27409 this.pinching = false;
27412 this.cropData = false;
27413 this.notifyEl.dom.innerHTML = this.emptyText;
27415 this.selectorEl.dom.value = '';
27419 resize : function()
27421 if(this.fireEvent('resize', this) != false){
27422 this.setThumbBoxPosition();
27423 this.setCanvasPosition();
27427 onFooterButtonClick : function(e, el, o, type)
27430 case 'rotate-left' :
27431 this.onRotateLeft(e);
27433 case 'rotate-right' :
27434 this.onRotateRight(e);
27437 this.beforeSelectFile(e);
27452 this.fireEvent('footerbuttonclick', this, type);
27455 beforeSelectFile : function(e)
27457 e.preventDefault();
27459 if(this.fireEvent('beforeselectfile', this) != false){
27460 this.selectorEl.dom.click();
27464 onFileSelected : function(e)
27466 e.preventDefault();
27468 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27472 var file = this.selectorEl.dom.files[0];
27474 if(this.fireEvent('inspect', this, file) != false){
27475 this.prepare(file);
27480 trash : function(e)
27482 this.fireEvent('trash', this);
27485 download : function(e)
27487 this.fireEvent('download', this);
27490 loadCanvas : function(src)
27492 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27496 this.imageEl = document.createElement('img');
27500 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27502 this.imageEl.src = src;
27506 onLoadCanvas : function()
27508 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27509 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27511 this.bodyEl.un('click', this.beforeSelectFile, this);
27513 this.notifyEl.hide();
27514 this.thumbEl.show();
27515 this.footerEl.show();
27517 this.baseRotateLevel();
27519 if(this.isDocument){
27520 this.setThumbBoxSize();
27523 this.setThumbBoxPosition();
27525 this.baseScaleLevel();
27531 this.canvasLoaded = true;
27534 this.maskEl.unmask();
27539 setCanvasPosition : function()
27541 if(!this.canvasEl){
27545 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27546 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27548 this.previewEl.setLeft(pw);
27549 this.previewEl.setTop(ph);
27553 onMouseDown : function(e)
27557 this.dragable = true;
27558 this.pinching = false;
27560 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27561 this.dragable = false;
27565 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27566 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27570 onMouseMove : function(e)
27574 if(!this.canvasLoaded){
27578 if (!this.dragable){
27582 var minX = Math.ceil(this.thumbEl.getLeft(true));
27583 var minY = Math.ceil(this.thumbEl.getTop(true));
27585 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27586 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27588 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27591 x = x - this.mouseX;
27592 y = y - this.mouseY;
27594 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27595 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27597 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27598 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27600 this.previewEl.setLeft(bgX);
27601 this.previewEl.setTop(bgY);
27603 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27604 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27607 onMouseUp : function(e)
27611 this.dragable = false;
27614 onMouseWheel : function(e)
27618 this.startScale = this.scale;
27620 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27622 if(!this.zoomable()){
27623 this.scale = this.startScale;
27632 zoomable : function()
27634 var minScale = this.thumbEl.getWidth() / this.minWidth;
27636 if(this.minWidth < this.minHeight){
27637 minScale = this.thumbEl.getHeight() / this.minHeight;
27640 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27641 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27645 (this.rotate == 0 || this.rotate == 180) &&
27647 width > this.imageEl.OriginWidth ||
27648 height > this.imageEl.OriginHeight ||
27649 (width < this.minWidth && height < this.minHeight)
27657 (this.rotate == 90 || this.rotate == 270) &&
27659 width > this.imageEl.OriginWidth ||
27660 height > this.imageEl.OriginHeight ||
27661 (width < this.minHeight && height < this.minWidth)
27668 !this.isDocument &&
27669 (this.rotate == 0 || this.rotate == 180) &&
27671 width < this.minWidth ||
27672 width > this.imageEl.OriginWidth ||
27673 height < this.minHeight ||
27674 height > this.imageEl.OriginHeight
27681 !this.isDocument &&
27682 (this.rotate == 90 || this.rotate == 270) &&
27684 width < this.minHeight ||
27685 width > this.imageEl.OriginWidth ||
27686 height < this.minWidth ||
27687 height > this.imageEl.OriginHeight
27697 onRotateLeft : function(e)
27699 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27701 var minScale = this.thumbEl.getWidth() / this.minWidth;
27703 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27704 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27706 this.startScale = this.scale;
27708 while (this.getScaleLevel() < minScale){
27710 this.scale = this.scale + 1;
27712 if(!this.zoomable()){
27717 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27718 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27723 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27730 this.scale = this.startScale;
27732 this.onRotateFail();
27737 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27739 if(this.isDocument){
27740 this.setThumbBoxSize();
27741 this.setThumbBoxPosition();
27742 this.setCanvasPosition();
27747 this.fireEvent('rotate', this, 'left');
27751 onRotateRight : function(e)
27753 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27755 var minScale = this.thumbEl.getWidth() / this.minWidth;
27757 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27758 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27760 this.startScale = this.scale;
27762 while (this.getScaleLevel() < minScale){
27764 this.scale = this.scale + 1;
27766 if(!this.zoomable()){
27771 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27772 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27777 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27784 this.scale = this.startScale;
27786 this.onRotateFail();
27791 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27793 if(this.isDocument){
27794 this.setThumbBoxSize();
27795 this.setThumbBoxPosition();
27796 this.setCanvasPosition();
27801 this.fireEvent('rotate', this, 'right');
27804 onRotateFail : function()
27806 this.errorEl.show(true);
27810 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27815 this.previewEl.dom.innerHTML = '';
27817 var canvasEl = document.createElement("canvas");
27819 var contextEl = canvasEl.getContext("2d");
27821 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27822 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27823 var center = this.imageEl.OriginWidth / 2;
27825 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27826 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27827 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27828 center = this.imageEl.OriginHeight / 2;
27831 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27833 contextEl.translate(center, center);
27834 contextEl.rotate(this.rotate * Math.PI / 180);
27836 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27838 this.canvasEl = document.createElement("canvas");
27840 this.contextEl = this.canvasEl.getContext("2d");
27842 switch (this.rotate) {
27845 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27846 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27848 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27853 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27854 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27856 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27857 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);
27861 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27866 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27867 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27869 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27870 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);
27874 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);
27879 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27880 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27882 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27887 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);
27894 this.previewEl.appendChild(this.canvasEl);
27896 this.setCanvasPosition();
27901 if(!this.canvasLoaded){
27905 var imageCanvas = document.createElement("canvas");
27907 var imageContext = imageCanvas.getContext("2d");
27909 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27910 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27912 var center = imageCanvas.width / 2;
27914 imageContext.translate(center, center);
27916 imageContext.rotate(this.rotate * Math.PI / 180);
27918 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27920 var canvas = document.createElement("canvas");
27922 var context = canvas.getContext("2d");
27924 canvas.width = this.minWidth;
27925 canvas.height = this.minHeight;
27927 switch (this.rotate) {
27930 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27931 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27933 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27934 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27936 var targetWidth = this.minWidth - 2 * x;
27937 var targetHeight = this.minHeight - 2 * y;
27941 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27942 scale = targetWidth / width;
27945 if(x > 0 && y == 0){
27946 scale = targetHeight / height;
27949 if(x > 0 && y > 0){
27950 scale = targetWidth / width;
27952 if(width < height){
27953 scale = targetHeight / height;
27957 context.scale(scale, scale);
27959 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27960 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27962 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27963 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27965 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27970 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27971 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27973 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27974 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27976 var targetWidth = this.minWidth - 2 * x;
27977 var targetHeight = this.minHeight - 2 * y;
27981 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27982 scale = targetWidth / width;
27985 if(x > 0 && y == 0){
27986 scale = targetHeight / height;
27989 if(x > 0 && y > 0){
27990 scale = targetWidth / width;
27992 if(width < height){
27993 scale = targetHeight / height;
27997 context.scale(scale, scale);
27999 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28000 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28002 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28003 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28005 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28007 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28012 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28013 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28015 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28016 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28018 var targetWidth = this.minWidth - 2 * x;
28019 var targetHeight = this.minHeight - 2 * y;
28023 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28024 scale = targetWidth / width;
28027 if(x > 0 && y == 0){
28028 scale = targetHeight / height;
28031 if(x > 0 && y > 0){
28032 scale = targetWidth / width;
28034 if(width < height){
28035 scale = targetHeight / height;
28039 context.scale(scale, scale);
28041 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28042 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28044 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28045 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28047 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28048 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28050 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28055 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28056 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28058 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28059 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28061 var targetWidth = this.minWidth - 2 * x;
28062 var targetHeight = this.minHeight - 2 * y;
28066 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28067 scale = targetWidth / width;
28070 if(x > 0 && y == 0){
28071 scale = targetHeight / height;
28074 if(x > 0 && y > 0){
28075 scale = targetWidth / width;
28077 if(width < height){
28078 scale = targetHeight / height;
28082 context.scale(scale, scale);
28084 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28085 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28087 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28088 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28090 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28092 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28099 this.cropData = canvas.toDataURL(this.cropType);
28101 if(this.fireEvent('crop', this, this.cropData) !== false){
28102 this.process(this.file, this.cropData);
28109 setThumbBoxSize : function()
28113 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28114 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28115 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28117 this.minWidth = width;
28118 this.minHeight = height;
28120 if(this.rotate == 90 || this.rotate == 270){
28121 this.minWidth = height;
28122 this.minHeight = width;
28127 width = Math.ceil(this.minWidth * height / this.minHeight);
28129 if(this.minWidth > this.minHeight){
28131 height = Math.ceil(this.minHeight * width / this.minWidth);
28134 this.thumbEl.setStyle({
28135 width : width + 'px',
28136 height : height + 'px'
28143 setThumbBoxPosition : function()
28145 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28146 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28148 this.thumbEl.setLeft(x);
28149 this.thumbEl.setTop(y);
28153 baseRotateLevel : function()
28155 this.baseRotate = 1;
28158 typeof(this.exif) != 'undefined' &&
28159 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28160 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28162 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28165 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28169 baseScaleLevel : function()
28173 if(this.isDocument){
28175 if(this.baseRotate == 6 || this.baseRotate == 8){
28177 height = this.thumbEl.getHeight();
28178 this.baseScale = height / this.imageEl.OriginWidth;
28180 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28181 width = this.thumbEl.getWidth();
28182 this.baseScale = width / this.imageEl.OriginHeight;
28188 height = this.thumbEl.getHeight();
28189 this.baseScale = height / this.imageEl.OriginHeight;
28191 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28192 width = this.thumbEl.getWidth();
28193 this.baseScale = width / this.imageEl.OriginWidth;
28199 if(this.baseRotate == 6 || this.baseRotate == 8){
28201 width = this.thumbEl.getHeight();
28202 this.baseScale = width / this.imageEl.OriginHeight;
28204 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28205 height = this.thumbEl.getWidth();
28206 this.baseScale = height / this.imageEl.OriginHeight;
28209 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28210 height = this.thumbEl.getWidth();
28211 this.baseScale = height / this.imageEl.OriginHeight;
28213 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28214 width = this.thumbEl.getHeight();
28215 this.baseScale = width / this.imageEl.OriginWidth;
28222 width = this.thumbEl.getWidth();
28223 this.baseScale = width / this.imageEl.OriginWidth;
28225 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28226 height = this.thumbEl.getHeight();
28227 this.baseScale = height / this.imageEl.OriginHeight;
28230 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28232 height = this.thumbEl.getHeight();
28233 this.baseScale = height / this.imageEl.OriginHeight;
28235 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28236 width = this.thumbEl.getWidth();
28237 this.baseScale = width / this.imageEl.OriginWidth;
28245 getScaleLevel : function()
28247 return this.baseScale * Math.pow(1.1, this.scale);
28250 onTouchStart : function(e)
28252 if(!this.canvasLoaded){
28253 this.beforeSelectFile(e);
28257 var touches = e.browserEvent.touches;
28263 if(touches.length == 1){
28264 this.onMouseDown(e);
28268 if(touches.length != 2){
28274 for(var i = 0, finger; finger = touches[i]; i++){
28275 coords.push(finger.pageX, finger.pageY);
28278 var x = Math.pow(coords[0] - coords[2], 2);
28279 var y = Math.pow(coords[1] - coords[3], 2);
28281 this.startDistance = Math.sqrt(x + y);
28283 this.startScale = this.scale;
28285 this.pinching = true;
28286 this.dragable = false;
28290 onTouchMove : function(e)
28292 if(!this.pinching && !this.dragable){
28296 var touches = e.browserEvent.touches;
28303 this.onMouseMove(e);
28309 for(var i = 0, finger; finger = touches[i]; i++){
28310 coords.push(finger.pageX, finger.pageY);
28313 var x = Math.pow(coords[0] - coords[2], 2);
28314 var y = Math.pow(coords[1] - coords[3], 2);
28316 this.endDistance = Math.sqrt(x + y);
28318 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28320 if(!this.zoomable()){
28321 this.scale = this.startScale;
28329 onTouchEnd : function(e)
28331 this.pinching = false;
28332 this.dragable = false;
28336 process : function(file, crop)
28339 this.maskEl.mask(this.loadingText);
28342 this.xhr = new XMLHttpRequest();
28344 file.xhr = this.xhr;
28346 this.xhr.open(this.method, this.url, true);
28349 "Accept": "application/json",
28350 "Cache-Control": "no-cache",
28351 "X-Requested-With": "XMLHttpRequest"
28354 for (var headerName in headers) {
28355 var headerValue = headers[headerName];
28357 this.xhr.setRequestHeader(headerName, headerValue);
28363 this.xhr.onload = function()
28365 _this.xhrOnLoad(_this.xhr);
28368 this.xhr.onerror = function()
28370 _this.xhrOnError(_this.xhr);
28373 var formData = new FormData();
28375 formData.append('returnHTML', 'NO');
28378 formData.append('crop', crop);
28381 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28382 formData.append(this.paramName, file, file.name);
28385 if(typeof(file.filename) != 'undefined'){
28386 formData.append('filename', file.filename);
28389 if(typeof(file.mimetype) != 'undefined'){
28390 formData.append('mimetype', file.mimetype);
28393 if(this.fireEvent('arrange', this, formData) != false){
28394 this.xhr.send(formData);
28398 xhrOnLoad : function(xhr)
28401 this.maskEl.unmask();
28404 if (xhr.readyState !== 4) {
28405 this.fireEvent('exception', this, xhr);
28409 var response = Roo.decode(xhr.responseText);
28411 if(!response.success){
28412 this.fireEvent('exception', this, xhr);
28416 var response = Roo.decode(xhr.responseText);
28418 this.fireEvent('upload', this, response);
28422 xhrOnError : function()
28425 this.maskEl.unmask();
28428 Roo.log('xhr on error');
28430 var response = Roo.decode(xhr.responseText);
28436 prepare : function(file)
28439 this.maskEl.mask(this.loadingText);
28445 if(typeof(file) === 'string'){
28446 this.loadCanvas(file);
28450 if(!file || !this.urlAPI){
28455 this.cropType = file.type;
28459 if(this.fireEvent('prepare', this, this.file) != false){
28461 var reader = new FileReader();
28463 reader.onload = function (e) {
28464 if (e.target.error) {
28465 Roo.log(e.target.error);
28469 var buffer = e.target.result,
28470 dataView = new DataView(buffer),
28472 maxOffset = dataView.byteLength - 4,
28476 if (dataView.getUint16(0) === 0xffd8) {
28477 while (offset < maxOffset) {
28478 markerBytes = dataView.getUint16(offset);
28480 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28481 markerLength = dataView.getUint16(offset + 2) + 2;
28482 if (offset + markerLength > dataView.byteLength) {
28483 Roo.log('Invalid meta data: Invalid segment size.');
28487 if(markerBytes == 0xffe1){
28488 _this.parseExifData(
28495 offset += markerLength;
28505 var url = _this.urlAPI.createObjectURL(_this.file);
28507 _this.loadCanvas(url);
28512 reader.readAsArrayBuffer(this.file);
28518 parseExifData : function(dataView, offset, length)
28520 var tiffOffset = offset + 10,
28524 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28525 // No Exif data, might be XMP data instead
28529 // Check for the ASCII code for "Exif" (0x45786966):
28530 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28531 // No Exif data, might be XMP data instead
28534 if (tiffOffset + 8 > dataView.byteLength) {
28535 Roo.log('Invalid Exif data: Invalid segment size.');
28538 // Check for the two null bytes:
28539 if (dataView.getUint16(offset + 8) !== 0x0000) {
28540 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28543 // Check the byte alignment:
28544 switch (dataView.getUint16(tiffOffset)) {
28546 littleEndian = true;
28549 littleEndian = false;
28552 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28555 // Check for the TIFF tag marker (0x002A):
28556 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28557 Roo.log('Invalid Exif data: Missing TIFF marker.');
28560 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28561 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28563 this.parseExifTags(
28566 tiffOffset + dirOffset,
28571 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28576 if (dirOffset + 6 > dataView.byteLength) {
28577 Roo.log('Invalid Exif data: Invalid directory offset.');
28580 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28581 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28582 if (dirEndOffset + 4 > dataView.byteLength) {
28583 Roo.log('Invalid Exif data: Invalid directory size.');
28586 for (i = 0; i < tagsNumber; i += 1) {
28590 dirOffset + 2 + 12 * i, // tag offset
28594 // Return the offset to the next directory:
28595 return dataView.getUint32(dirEndOffset, littleEndian);
28598 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28600 var tag = dataView.getUint16(offset, littleEndian);
28602 this.exif[tag] = this.getExifValue(
28606 dataView.getUint16(offset + 2, littleEndian), // tag type
28607 dataView.getUint32(offset + 4, littleEndian), // tag length
28612 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28614 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28623 Roo.log('Invalid Exif data: Invalid tag type.');
28627 tagSize = tagType.size * length;
28628 // Determine if the value is contained in the dataOffset bytes,
28629 // or if the value at the dataOffset is a pointer to the actual data:
28630 dataOffset = tagSize > 4 ?
28631 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28632 if (dataOffset + tagSize > dataView.byteLength) {
28633 Roo.log('Invalid Exif data: Invalid data offset.');
28636 if (length === 1) {
28637 return tagType.getValue(dataView, dataOffset, littleEndian);
28640 for (i = 0; i < length; i += 1) {
28641 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28644 if (tagType.ascii) {
28646 // Concatenate the chars:
28647 for (i = 0; i < values.length; i += 1) {
28649 // Ignore the terminating NULL byte(s):
28650 if (c === '\u0000') {
28662 Roo.apply(Roo.bootstrap.UploadCropbox, {
28664 'Orientation': 0x0112
28668 1: 0, //'top-left',
28670 3: 180, //'bottom-right',
28671 // 4: 'bottom-left',
28673 6: 90, //'right-top',
28674 // 7: 'right-bottom',
28675 8: 270 //'left-bottom'
28679 // byte, 8-bit unsigned int:
28681 getValue: function (dataView, dataOffset) {
28682 return dataView.getUint8(dataOffset);
28686 // ascii, 8-bit byte:
28688 getValue: function (dataView, dataOffset) {
28689 return String.fromCharCode(dataView.getUint8(dataOffset));
28694 // short, 16 bit int:
28696 getValue: function (dataView, dataOffset, littleEndian) {
28697 return dataView.getUint16(dataOffset, littleEndian);
28701 // long, 32 bit int:
28703 getValue: function (dataView, dataOffset, littleEndian) {
28704 return dataView.getUint32(dataOffset, littleEndian);
28708 // rational = two long values, first is numerator, second is denominator:
28710 getValue: function (dataView, dataOffset, littleEndian) {
28711 return dataView.getUint32(dataOffset, littleEndian) /
28712 dataView.getUint32(dataOffset + 4, littleEndian);
28716 // slong, 32 bit signed int:
28718 getValue: function (dataView, dataOffset, littleEndian) {
28719 return dataView.getInt32(dataOffset, littleEndian);
28723 // srational, two slongs, first is numerator, second is denominator:
28725 getValue: function (dataView, dataOffset, littleEndian) {
28726 return dataView.getInt32(dataOffset, littleEndian) /
28727 dataView.getInt32(dataOffset + 4, littleEndian);
28737 cls : 'btn-group roo-upload-cropbox-rotate-left',
28738 action : 'rotate-left',
28742 cls : 'btn btn-default',
28743 html : '<i class="fa fa-undo"></i>'
28749 cls : 'btn-group roo-upload-cropbox-picture',
28750 action : 'picture',
28754 cls : 'btn btn-default',
28755 html : '<i class="fa fa-picture-o"></i>'
28761 cls : 'btn-group roo-upload-cropbox-rotate-right',
28762 action : 'rotate-right',
28766 cls : 'btn btn-default',
28767 html : '<i class="fa fa-repeat"></i>'
28775 cls : 'btn-group roo-upload-cropbox-rotate-left',
28776 action : 'rotate-left',
28780 cls : 'btn btn-default',
28781 html : '<i class="fa fa-undo"></i>'
28787 cls : 'btn-group roo-upload-cropbox-download',
28788 action : 'download',
28792 cls : 'btn btn-default',
28793 html : '<i class="fa fa-download"></i>'
28799 cls : 'btn-group roo-upload-cropbox-crop',
28804 cls : 'btn btn-default',
28805 html : '<i class="fa fa-crop"></i>'
28811 cls : 'btn-group roo-upload-cropbox-trash',
28816 cls : 'btn btn-default',
28817 html : '<i class="fa fa-trash"></i>'
28823 cls : 'btn-group roo-upload-cropbox-rotate-right',
28824 action : 'rotate-right',
28828 cls : 'btn btn-default',
28829 html : '<i class="fa fa-repeat"></i>'
28837 cls : 'btn-group roo-upload-cropbox-rotate-left',
28838 action : 'rotate-left',
28842 cls : 'btn btn-default',
28843 html : '<i class="fa fa-undo"></i>'
28849 cls : 'btn-group roo-upload-cropbox-rotate-right',
28850 action : 'rotate-right',
28854 cls : 'btn btn-default',
28855 html : '<i class="fa fa-repeat"></i>'
28868 * @class Roo.bootstrap.DocumentManager
28869 * @extends Roo.bootstrap.Component
28870 * Bootstrap DocumentManager class
28871 * @cfg {String} paramName default 'imageUpload'
28872 * @cfg {String} toolTipName default 'filename'
28873 * @cfg {String} method default POST
28874 * @cfg {String} url action url
28875 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28876 * @cfg {Boolean} multiple multiple upload default true
28877 * @cfg {Number} thumbSize default 300
28878 * @cfg {String} fieldLabel
28879 * @cfg {Number} labelWidth default 4
28880 * @cfg {String} labelAlign (left|top) default left
28881 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28882 * @cfg {Number} labellg set the width of label (1-12)
28883 * @cfg {Number} labelmd set the width of label (1-12)
28884 * @cfg {Number} labelsm set the width of label (1-12)
28885 * @cfg {Number} labelxs set the width of label (1-12)
28888 * Create a new DocumentManager
28889 * @param {Object} config The config object
28892 Roo.bootstrap.DocumentManager = function(config){
28893 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28896 this.delegates = [];
28901 * Fire when initial the DocumentManager
28902 * @param {Roo.bootstrap.DocumentManager} this
28907 * inspect selected file
28908 * @param {Roo.bootstrap.DocumentManager} this
28909 * @param {File} file
28914 * Fire when xhr load exception
28915 * @param {Roo.bootstrap.DocumentManager} this
28916 * @param {XMLHttpRequest} xhr
28918 "exception" : true,
28920 * @event afterupload
28921 * Fire when xhr load exception
28922 * @param {Roo.bootstrap.DocumentManager} this
28923 * @param {XMLHttpRequest} xhr
28925 "afterupload" : true,
28928 * prepare the form data
28929 * @param {Roo.bootstrap.DocumentManager} this
28930 * @param {Object} formData
28935 * Fire when remove the file
28936 * @param {Roo.bootstrap.DocumentManager} this
28937 * @param {Object} file
28942 * Fire after refresh the file
28943 * @param {Roo.bootstrap.DocumentManager} this
28948 * Fire after click the image
28949 * @param {Roo.bootstrap.DocumentManager} this
28950 * @param {Object} file
28955 * Fire when upload a image and editable set to true
28956 * @param {Roo.bootstrap.DocumentManager} this
28957 * @param {Object} file
28961 * @event beforeselectfile
28962 * Fire before select file
28963 * @param {Roo.bootstrap.DocumentManager} this
28965 "beforeselectfile" : true,
28968 * Fire before process file
28969 * @param {Roo.bootstrap.DocumentManager} this
28970 * @param {Object} file
28974 * @event previewrendered
28975 * Fire when preview rendered
28976 * @param {Roo.bootstrap.DocumentManager} this
28977 * @param {Object} file
28979 "previewrendered" : true,
28982 "previewResize" : true
28987 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28996 paramName : 'imageUpload',
28997 toolTipName : 'filename',
29000 labelAlign : 'left',
29010 getAutoCreate : function()
29012 var managerWidget = {
29014 cls : 'roo-document-manager',
29018 cls : 'roo-document-manager-selector',
29023 cls : 'roo-document-manager-uploader',
29027 cls : 'roo-document-manager-upload-btn',
29028 html : '<i class="fa fa-plus"></i>'
29039 cls : 'column col-md-12',
29044 if(this.fieldLabel.length){
29049 cls : 'column col-md-12',
29050 html : this.fieldLabel
29054 cls : 'column col-md-12',
29059 if(this.labelAlign == 'left'){
29064 html : this.fieldLabel
29073 if(this.labelWidth > 12){
29074 content[0].style = "width: " + this.labelWidth + 'px';
29077 if(this.labelWidth < 13 && this.labelmd == 0){
29078 this.labelmd = this.labelWidth;
29081 if(this.labellg > 0){
29082 content[0].cls += ' col-lg-' + this.labellg;
29083 content[1].cls += ' col-lg-' + (12 - this.labellg);
29086 if(this.labelmd > 0){
29087 content[0].cls += ' col-md-' + this.labelmd;
29088 content[1].cls += ' col-md-' + (12 - this.labelmd);
29091 if(this.labelsm > 0){
29092 content[0].cls += ' col-sm-' + this.labelsm;
29093 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29096 if(this.labelxs > 0){
29097 content[0].cls += ' col-xs-' + this.labelxs;
29098 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29106 cls : 'row clearfix',
29114 initEvents : function()
29116 this.managerEl = this.el.select('.roo-document-manager', true).first();
29117 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29119 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29120 this.selectorEl.hide();
29123 this.selectorEl.attr('multiple', 'multiple');
29126 this.selectorEl.on('change', this.onFileSelected, this);
29128 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29129 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29131 this.uploader.on('click', this.onUploaderClick, this);
29133 this.renderProgressDialog();
29137 window.addEventListener("resize", function() { _this.refresh(); } );
29139 this.fireEvent('initial', this);
29142 renderProgressDialog : function()
29146 this.progressDialog = new Roo.bootstrap.Modal({
29147 cls : 'roo-document-manager-progress-dialog',
29148 allow_close : false,
29158 btnclick : function() {
29159 _this.uploadCancel();
29165 this.progressDialog.render(Roo.get(document.body));
29167 this.progress = new Roo.bootstrap.Progress({
29168 cls : 'roo-document-manager-progress',
29173 this.progress.render(this.progressDialog.getChildContainer());
29175 this.progressBar = new Roo.bootstrap.ProgressBar({
29176 cls : 'roo-document-manager-progress-bar',
29179 aria_valuemax : 12,
29183 this.progressBar.render(this.progress.getChildContainer());
29186 onUploaderClick : function(e)
29188 e.preventDefault();
29190 if(this.fireEvent('beforeselectfile', this) != false){
29191 this.selectorEl.dom.click();
29196 onFileSelected : function(e)
29198 e.preventDefault();
29200 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29204 Roo.each(this.selectorEl.dom.files, function(file){
29205 if(this.fireEvent('inspect', this, file) != false){
29206 this.files.push(file);
29216 this.selectorEl.dom.value = '';
29218 if(!this.files || !this.files.length){
29222 if(this.boxes > 0 && this.files.length > this.boxes){
29223 this.files = this.files.slice(0, this.boxes);
29226 this.uploader.show();
29228 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29229 this.uploader.hide();
29238 Roo.each(this.files, function(file){
29240 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29241 var f = this.renderPreview(file);
29246 if(file.type.indexOf('image') != -1){
29247 this.delegates.push(
29249 _this.process(file);
29250 }).createDelegate(this)
29258 _this.process(file);
29259 }).createDelegate(this)
29264 this.files = files;
29266 this.delegates = this.delegates.concat(docs);
29268 if(!this.delegates.length){
29273 this.progressBar.aria_valuemax = this.delegates.length;
29280 arrange : function()
29282 if(!this.delegates.length){
29283 this.progressDialog.hide();
29288 var delegate = this.delegates.shift();
29290 this.progressDialog.show();
29292 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29294 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29299 refresh : function()
29301 this.uploader.show();
29303 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29304 this.uploader.hide();
29307 Roo.isTouch ? this.closable(false) : this.closable(true);
29309 this.fireEvent('refresh', this);
29312 onRemove : function(e, el, o)
29314 e.preventDefault();
29316 this.fireEvent('remove', this, o);
29320 remove : function(o)
29324 Roo.each(this.files, function(file){
29325 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29334 this.files = files;
29341 Roo.each(this.files, function(file){
29346 file.target.remove();
29355 onClick : function(e, el, o)
29357 e.preventDefault();
29359 this.fireEvent('click', this, o);
29363 closable : function(closable)
29365 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29367 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29379 xhrOnLoad : function(xhr)
29381 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29385 if (xhr.readyState !== 4) {
29387 this.fireEvent('exception', this, xhr);
29391 var response = Roo.decode(xhr.responseText);
29393 if(!response.success){
29395 this.fireEvent('exception', this, xhr);
29399 var file = this.renderPreview(response.data);
29401 this.files.push(file);
29405 this.fireEvent('afterupload', this, xhr);
29409 xhrOnError : function(xhr)
29411 Roo.log('xhr on error');
29413 var response = Roo.decode(xhr.responseText);
29420 process : function(file)
29422 if(this.fireEvent('process', this, file) !== false){
29423 if(this.editable && file.type.indexOf('image') != -1){
29424 this.fireEvent('edit', this, file);
29428 this.uploadStart(file, false);
29435 uploadStart : function(file, crop)
29437 this.xhr = new XMLHttpRequest();
29439 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29444 file.xhr = this.xhr;
29446 this.managerEl.createChild({
29448 cls : 'roo-document-manager-loading',
29452 tooltip : file.name,
29453 cls : 'roo-document-manager-thumb',
29454 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29460 this.xhr.open(this.method, this.url, true);
29463 "Accept": "application/json",
29464 "Cache-Control": "no-cache",
29465 "X-Requested-With": "XMLHttpRequest"
29468 for (var headerName in headers) {
29469 var headerValue = headers[headerName];
29471 this.xhr.setRequestHeader(headerName, headerValue);
29477 this.xhr.onload = function()
29479 _this.xhrOnLoad(_this.xhr);
29482 this.xhr.onerror = function()
29484 _this.xhrOnError(_this.xhr);
29487 var formData = new FormData();
29489 formData.append('returnHTML', 'NO');
29492 formData.append('crop', crop);
29495 formData.append(this.paramName, file, file.name);
29502 if(this.fireEvent('prepare', this, formData, options) != false){
29504 if(options.manually){
29508 this.xhr.send(formData);
29512 this.uploadCancel();
29515 uploadCancel : function()
29521 this.delegates = [];
29523 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29530 renderPreview : function(file)
29532 if(typeof(file.target) != 'undefined' && file.target){
29536 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29538 var previewEl = this.managerEl.createChild({
29540 cls : 'roo-document-manager-preview',
29544 tooltip : file[this.toolTipName],
29545 cls : 'roo-document-manager-thumb',
29546 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29551 html : '<i class="fa fa-times-circle"></i>'
29556 var close = previewEl.select('button.close', true).first();
29558 close.on('click', this.onRemove, this, file);
29560 file.target = previewEl;
29562 var image = previewEl.select('img', true).first();
29566 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29568 image.on('click', this.onClick, this, file);
29570 this.fireEvent('previewrendered', this, file);
29576 onPreviewLoad : function(file, image)
29578 if(typeof(file.target) == 'undefined' || !file.target){
29582 var width = image.dom.naturalWidth || image.dom.width;
29583 var height = image.dom.naturalHeight || image.dom.height;
29585 if(!this.previewResize) {
29589 if(width > height){
29590 file.target.addClass('wide');
29594 file.target.addClass('tall');
29599 uploadFromSource : function(file, crop)
29601 this.xhr = new XMLHttpRequest();
29603 this.managerEl.createChild({
29605 cls : 'roo-document-manager-loading',
29609 tooltip : file.name,
29610 cls : 'roo-document-manager-thumb',
29611 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29617 this.xhr.open(this.method, this.url, true);
29620 "Accept": "application/json",
29621 "Cache-Control": "no-cache",
29622 "X-Requested-With": "XMLHttpRequest"
29625 for (var headerName in headers) {
29626 var headerValue = headers[headerName];
29628 this.xhr.setRequestHeader(headerName, headerValue);
29634 this.xhr.onload = function()
29636 _this.xhrOnLoad(_this.xhr);
29639 this.xhr.onerror = function()
29641 _this.xhrOnError(_this.xhr);
29644 var formData = new FormData();
29646 formData.append('returnHTML', 'NO');
29648 formData.append('crop', crop);
29650 if(typeof(file.filename) != 'undefined'){
29651 formData.append('filename', file.filename);
29654 if(typeof(file.mimetype) != 'undefined'){
29655 formData.append('mimetype', file.mimetype);
29660 if(this.fireEvent('prepare', this, formData) != false){
29661 this.xhr.send(formData);
29671 * @class Roo.bootstrap.DocumentViewer
29672 * @extends Roo.bootstrap.Component
29673 * Bootstrap DocumentViewer class
29674 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29675 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29678 * Create a new DocumentViewer
29679 * @param {Object} config The config object
29682 Roo.bootstrap.DocumentViewer = function(config){
29683 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29688 * Fire after initEvent
29689 * @param {Roo.bootstrap.DocumentViewer} this
29695 * @param {Roo.bootstrap.DocumentViewer} this
29700 * Fire after download button
29701 * @param {Roo.bootstrap.DocumentViewer} this
29706 * Fire after trash button
29707 * @param {Roo.bootstrap.DocumentViewer} this
29714 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29716 showDownload : true,
29720 getAutoCreate : function()
29724 cls : 'roo-document-viewer',
29728 cls : 'roo-document-viewer-body',
29732 cls : 'roo-document-viewer-thumb',
29736 cls : 'roo-document-viewer-image'
29744 cls : 'roo-document-viewer-footer',
29747 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29751 cls : 'btn-group roo-document-viewer-download',
29755 cls : 'btn btn-default',
29756 html : '<i class="fa fa-download"></i>'
29762 cls : 'btn-group roo-document-viewer-trash',
29766 cls : 'btn btn-default',
29767 html : '<i class="fa fa-trash"></i>'
29780 initEvents : function()
29782 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29783 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29785 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29786 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29788 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29789 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29791 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29792 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29794 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29795 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29797 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29798 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29800 this.bodyEl.on('click', this.onClick, this);
29801 this.downloadBtn.on('click', this.onDownload, this);
29802 this.trashBtn.on('click', this.onTrash, this);
29804 this.downloadBtn.hide();
29805 this.trashBtn.hide();
29807 if(this.showDownload){
29808 this.downloadBtn.show();
29811 if(this.showTrash){
29812 this.trashBtn.show();
29815 if(!this.showDownload && !this.showTrash) {
29816 this.footerEl.hide();
29821 initial : function()
29823 this.fireEvent('initial', this);
29827 onClick : function(e)
29829 e.preventDefault();
29831 this.fireEvent('click', this);
29834 onDownload : function(e)
29836 e.preventDefault();
29838 this.fireEvent('download', this);
29841 onTrash : function(e)
29843 e.preventDefault();
29845 this.fireEvent('trash', this);
29857 * @class Roo.bootstrap.NavProgressBar
29858 * @extends Roo.bootstrap.Component
29859 * Bootstrap NavProgressBar class
29862 * Create a new nav progress bar
29863 * @param {Object} config The config object
29866 Roo.bootstrap.NavProgressBar = function(config){
29867 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29869 this.bullets = this.bullets || [];
29871 // Roo.bootstrap.NavProgressBar.register(this);
29875 * Fires when the active item changes
29876 * @param {Roo.bootstrap.NavProgressBar} this
29877 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29878 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29885 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29890 getAutoCreate : function()
29892 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29896 cls : 'roo-navigation-bar-group',
29900 cls : 'roo-navigation-top-bar'
29904 cls : 'roo-navigation-bullets-bar',
29908 cls : 'roo-navigation-bar'
29915 cls : 'roo-navigation-bottom-bar'
29925 initEvents: function()
29930 onRender : function(ct, position)
29932 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29934 if(this.bullets.length){
29935 Roo.each(this.bullets, function(b){
29944 addItem : function(cfg)
29946 var item = new Roo.bootstrap.NavProgressItem(cfg);
29948 item.parentId = this.id;
29949 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29952 var top = new Roo.bootstrap.Element({
29954 cls : 'roo-navigation-bar-text'
29957 var bottom = new Roo.bootstrap.Element({
29959 cls : 'roo-navigation-bar-text'
29962 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29963 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29965 var topText = new Roo.bootstrap.Element({
29967 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29970 var bottomText = new Roo.bootstrap.Element({
29972 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29975 topText.onRender(top.el, null);
29976 bottomText.onRender(bottom.el, null);
29979 item.bottomEl = bottom;
29982 this.barItems.push(item);
29987 getActive : function()
29989 var active = false;
29991 Roo.each(this.barItems, function(v){
29993 if (!v.isActive()) {
30005 setActiveItem : function(item)
30009 Roo.each(this.barItems, function(v){
30010 if (v.rid == item.rid) {
30014 if (v.isActive()) {
30015 v.setActive(false);
30020 item.setActive(true);
30022 this.fireEvent('changed', this, item, prev);
30025 getBarItem: function(rid)
30029 Roo.each(this.barItems, function(e) {
30030 if (e.rid != rid) {
30041 indexOfItem : function(item)
30045 Roo.each(this.barItems, function(v, i){
30047 if (v.rid != item.rid) {
30058 setActiveNext : function()
30060 var i = this.indexOfItem(this.getActive());
30062 if (i > this.barItems.length) {
30066 this.setActiveItem(this.barItems[i+1]);
30069 setActivePrev : function()
30071 var i = this.indexOfItem(this.getActive());
30077 this.setActiveItem(this.barItems[i-1]);
30080 format : function()
30082 if(!this.barItems.length){
30086 var width = 100 / this.barItems.length;
30088 Roo.each(this.barItems, function(i){
30089 i.el.setStyle('width', width + '%');
30090 i.topEl.el.setStyle('width', width + '%');
30091 i.bottomEl.el.setStyle('width', width + '%');
30100 * Nav Progress Item
30105 * @class Roo.bootstrap.NavProgressItem
30106 * @extends Roo.bootstrap.Component
30107 * Bootstrap NavProgressItem class
30108 * @cfg {String} rid the reference id
30109 * @cfg {Boolean} active (true|false) Is item active default false
30110 * @cfg {Boolean} disabled (true|false) Is item active default false
30111 * @cfg {String} html
30112 * @cfg {String} position (top|bottom) text position default bottom
30113 * @cfg {String} icon show icon instead of number
30116 * Create a new NavProgressItem
30117 * @param {Object} config The config object
30119 Roo.bootstrap.NavProgressItem = function(config){
30120 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30125 * The raw click event for the entire grid.
30126 * @param {Roo.bootstrap.NavProgressItem} this
30127 * @param {Roo.EventObject} e
30134 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30140 position : 'bottom',
30143 getAutoCreate : function()
30145 var iconCls = 'roo-navigation-bar-item-icon';
30147 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30151 cls: 'roo-navigation-bar-item',
30161 cfg.cls += ' active';
30164 cfg.cls += ' disabled';
30170 disable : function()
30172 this.setDisabled(true);
30175 enable : function()
30177 this.setDisabled(false);
30180 initEvents: function()
30182 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30184 this.iconEl.on('click', this.onClick, this);
30187 onClick : function(e)
30189 e.preventDefault();
30195 if(this.fireEvent('click', this, e) === false){
30199 this.parent().setActiveItem(this);
30202 isActive: function ()
30204 return this.active;
30207 setActive : function(state)
30209 if(this.active == state){
30213 this.active = state;
30216 this.el.addClass('active');
30220 this.el.removeClass('active');
30225 setDisabled : function(state)
30227 if(this.disabled == state){
30231 this.disabled = state;
30234 this.el.addClass('disabled');
30238 this.el.removeClass('disabled');
30241 tooltipEl : function()
30243 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30256 * @class Roo.bootstrap.FieldLabel
30257 * @extends Roo.bootstrap.Component
30258 * Bootstrap FieldLabel class
30259 * @cfg {String} html contents of the element
30260 * @cfg {String} tag tag of the element default label
30261 * @cfg {String} cls class of the element
30262 * @cfg {String} target label target
30263 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30264 * @cfg {String} invalidClass default "text-warning"
30265 * @cfg {String} validClass default "text-success"
30266 * @cfg {String} iconTooltip default "This field is required"
30267 * @cfg {String} indicatorpos (left|right) default left
30270 * Create a new FieldLabel
30271 * @param {Object} config The config object
30274 Roo.bootstrap.FieldLabel = function(config){
30275 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30280 * Fires after the field has been marked as invalid.
30281 * @param {Roo.form.FieldLabel} this
30282 * @param {String} msg The validation message
30287 * Fires after the field has been validated with no errors.
30288 * @param {Roo.form.FieldLabel} this
30294 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30301 invalidClass : 'has-warning',
30302 validClass : 'has-success',
30303 iconTooltip : 'This field is required',
30304 indicatorpos : 'left',
30306 getAutoCreate : function(){
30309 if (!this.allowBlank) {
30315 cls : 'roo-bootstrap-field-label ' + this.cls,
30320 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30321 tooltip : this.iconTooltip
30330 if(this.indicatorpos == 'right'){
30333 cls : 'roo-bootstrap-field-label ' + this.cls,
30342 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30343 tooltip : this.iconTooltip
30352 initEvents: function()
30354 Roo.bootstrap.Element.superclass.initEvents.call(this);
30356 this.indicator = this.indicatorEl();
30358 if(this.indicator){
30359 this.indicator.removeClass('visible');
30360 this.indicator.addClass('invisible');
30363 Roo.bootstrap.FieldLabel.register(this);
30366 indicatorEl : function()
30368 var indicator = this.el.select('i.roo-required-indicator',true).first();
30379 * Mark this field as valid
30381 markValid : function()
30383 if(this.indicator){
30384 this.indicator.removeClass('visible');
30385 this.indicator.addClass('invisible');
30388 this.el.removeClass(this.invalidClass);
30390 this.el.addClass(this.validClass);
30392 this.fireEvent('valid', this);
30396 * Mark this field as invalid
30397 * @param {String} msg The validation message
30399 markInvalid : function(msg)
30401 if(this.indicator){
30402 this.indicator.removeClass('invisible');
30403 this.indicator.addClass('visible');
30406 this.el.removeClass(this.validClass);
30408 this.el.addClass(this.invalidClass);
30410 this.fireEvent('invalid', this, msg);
30416 Roo.apply(Roo.bootstrap.FieldLabel, {
30421 * register a FieldLabel Group
30422 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30424 register : function(label)
30426 if(this.groups.hasOwnProperty(label.target)){
30430 this.groups[label.target] = label;
30434 * fetch a FieldLabel Group based on the target
30435 * @param {string} target
30436 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30438 get: function(target) {
30439 if (typeof(this.groups[target]) == 'undefined') {
30443 return this.groups[target] ;
30452 * page DateSplitField.
30458 * @class Roo.bootstrap.DateSplitField
30459 * @extends Roo.bootstrap.Component
30460 * Bootstrap DateSplitField class
30461 * @cfg {string} fieldLabel - the label associated
30462 * @cfg {Number} labelWidth set the width of label (0-12)
30463 * @cfg {String} labelAlign (top|left)
30464 * @cfg {Boolean} dayAllowBlank (true|false) default false
30465 * @cfg {Boolean} monthAllowBlank (true|false) default false
30466 * @cfg {Boolean} yearAllowBlank (true|false) default false
30467 * @cfg {string} dayPlaceholder
30468 * @cfg {string} monthPlaceholder
30469 * @cfg {string} yearPlaceholder
30470 * @cfg {string} dayFormat default 'd'
30471 * @cfg {string} monthFormat default 'm'
30472 * @cfg {string} yearFormat default 'Y'
30473 * @cfg {Number} labellg set the width of label (1-12)
30474 * @cfg {Number} labelmd set the width of label (1-12)
30475 * @cfg {Number} labelsm set the width of label (1-12)
30476 * @cfg {Number} labelxs set the width of label (1-12)
30480 * Create a new DateSplitField
30481 * @param {Object} config The config object
30484 Roo.bootstrap.DateSplitField = function(config){
30485 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30491 * getting the data of years
30492 * @param {Roo.bootstrap.DateSplitField} this
30493 * @param {Object} years
30498 * getting the data of days
30499 * @param {Roo.bootstrap.DateSplitField} this
30500 * @param {Object} days
30505 * Fires after the field has been marked as invalid.
30506 * @param {Roo.form.Field} this
30507 * @param {String} msg The validation message
30512 * Fires after the field has been validated with no errors.
30513 * @param {Roo.form.Field} this
30519 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30522 labelAlign : 'top',
30524 dayAllowBlank : false,
30525 monthAllowBlank : false,
30526 yearAllowBlank : false,
30527 dayPlaceholder : '',
30528 monthPlaceholder : '',
30529 yearPlaceholder : '',
30533 isFormField : true,
30539 getAutoCreate : function()
30543 cls : 'row roo-date-split-field-group',
30548 cls : 'form-hidden-field roo-date-split-field-group-value',
30554 var labelCls = 'col-md-12';
30555 var contentCls = 'col-md-4';
30557 if(this.fieldLabel){
30561 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30565 html : this.fieldLabel
30570 if(this.labelAlign == 'left'){
30572 if(this.labelWidth > 12){
30573 label.style = "width: " + this.labelWidth + 'px';
30576 if(this.labelWidth < 13 && this.labelmd == 0){
30577 this.labelmd = this.labelWidth;
30580 if(this.labellg > 0){
30581 labelCls = ' col-lg-' + this.labellg;
30582 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30585 if(this.labelmd > 0){
30586 labelCls = ' col-md-' + this.labelmd;
30587 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30590 if(this.labelsm > 0){
30591 labelCls = ' col-sm-' + this.labelsm;
30592 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30595 if(this.labelxs > 0){
30596 labelCls = ' col-xs-' + this.labelxs;
30597 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30601 label.cls += ' ' + labelCls;
30603 cfg.cn.push(label);
30606 Roo.each(['day', 'month', 'year'], function(t){
30609 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30616 inputEl: function ()
30618 return this.el.select('.roo-date-split-field-group-value', true).first();
30621 onRender : function(ct, position)
30625 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30627 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30629 this.dayField = new Roo.bootstrap.ComboBox({
30630 allowBlank : this.dayAllowBlank,
30631 alwaysQuery : true,
30632 displayField : 'value',
30635 forceSelection : true,
30637 placeholder : this.dayPlaceholder,
30638 selectOnFocus : true,
30639 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30640 triggerAction : 'all',
30642 valueField : 'value',
30643 store : new Roo.data.SimpleStore({
30644 data : (function() {
30646 _this.fireEvent('days', _this, days);
30649 fields : [ 'value' ]
30652 select : function (_self, record, index)
30654 _this.setValue(_this.getValue());
30659 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30661 this.monthField = new Roo.bootstrap.MonthField({
30662 after : '<i class=\"fa fa-calendar\"></i>',
30663 allowBlank : this.monthAllowBlank,
30664 placeholder : this.monthPlaceholder,
30667 render : function (_self)
30669 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30670 e.preventDefault();
30674 select : function (_self, oldvalue, newvalue)
30676 _this.setValue(_this.getValue());
30681 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30683 this.yearField = new Roo.bootstrap.ComboBox({
30684 allowBlank : this.yearAllowBlank,
30685 alwaysQuery : true,
30686 displayField : 'value',
30689 forceSelection : true,
30691 placeholder : this.yearPlaceholder,
30692 selectOnFocus : true,
30693 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30694 triggerAction : 'all',
30696 valueField : 'value',
30697 store : new Roo.data.SimpleStore({
30698 data : (function() {
30700 _this.fireEvent('years', _this, years);
30703 fields : [ 'value' ]
30706 select : function (_self, record, index)
30708 _this.setValue(_this.getValue());
30713 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30716 setValue : function(v, format)
30718 this.inputEl.dom.value = v;
30720 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30722 var d = Date.parseDate(v, f);
30729 this.setDay(d.format(this.dayFormat));
30730 this.setMonth(d.format(this.monthFormat));
30731 this.setYear(d.format(this.yearFormat));
30738 setDay : function(v)
30740 this.dayField.setValue(v);
30741 this.inputEl.dom.value = this.getValue();
30746 setMonth : function(v)
30748 this.monthField.setValue(v, true);
30749 this.inputEl.dom.value = this.getValue();
30754 setYear : function(v)
30756 this.yearField.setValue(v);
30757 this.inputEl.dom.value = this.getValue();
30762 getDay : function()
30764 return this.dayField.getValue();
30767 getMonth : function()
30769 return this.monthField.getValue();
30772 getYear : function()
30774 return this.yearField.getValue();
30777 getValue : function()
30779 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30781 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30791 this.inputEl.dom.value = '';
30796 validate : function()
30798 var d = this.dayField.validate();
30799 var m = this.monthField.validate();
30800 var y = this.yearField.validate();
30805 (!this.dayAllowBlank && !d) ||
30806 (!this.monthAllowBlank && !m) ||
30807 (!this.yearAllowBlank && !y)
30812 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30821 this.markInvalid();
30826 markValid : function()
30829 var label = this.el.select('label', true).first();
30830 var icon = this.el.select('i.fa-star', true).first();
30836 this.fireEvent('valid', this);
30840 * Mark this field as invalid
30841 * @param {String} msg The validation message
30843 markInvalid : function(msg)
30846 var label = this.el.select('label', true).first();
30847 var icon = this.el.select('i.fa-star', true).first();
30849 if(label && !icon){
30850 this.el.select('.roo-date-split-field-label', true).createChild({
30852 cls : 'text-danger fa fa-lg fa-star',
30853 tooltip : 'This field is required',
30854 style : 'margin-right:5px;'
30858 this.fireEvent('invalid', this, msg);
30861 clearInvalid : function()
30863 var label = this.el.select('label', true).first();
30864 var icon = this.el.select('i.fa-star', true).first();
30870 this.fireEvent('valid', this);
30873 getName: function()
30883 * http://masonry.desandro.com
30885 * The idea is to render all the bricks based on vertical width...
30887 * The original code extends 'outlayer' - we might need to use that....
30893 * @class Roo.bootstrap.LayoutMasonry
30894 * @extends Roo.bootstrap.Component
30895 * Bootstrap Layout Masonry class
30898 * Create a new Element
30899 * @param {Object} config The config object
30902 Roo.bootstrap.LayoutMasonry = function(config){
30904 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30908 Roo.bootstrap.LayoutMasonry.register(this);
30914 * Fire after layout the items
30915 * @param {Roo.bootstrap.LayoutMasonry} this
30916 * @param {Roo.EventObject} e
30923 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30926 * @cfg {Boolean} isLayoutInstant = no animation?
30928 isLayoutInstant : false, // needed?
30931 * @cfg {Number} boxWidth width of the columns
30936 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30941 * @cfg {Number} padWidth padding below box..
30946 * @cfg {Number} gutter gutter width..
30951 * @cfg {Number} maxCols maximum number of columns
30957 * @cfg {Boolean} isAutoInitial defalut true
30959 isAutoInitial : true,
30964 * @cfg {Boolean} isHorizontal defalut false
30966 isHorizontal : false,
30968 currentSize : null,
30974 bricks: null, //CompositeElement
30978 _isLayoutInited : false,
30980 // isAlternative : false, // only use for vertical layout...
30983 * @cfg {Number} alternativePadWidth padding below box..
30985 alternativePadWidth : 50,
30987 selectedBrick : [],
30989 getAutoCreate : function(){
30991 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30995 cls: 'blog-masonary-wrapper ' + this.cls,
30997 cls : 'mas-boxes masonary'
31004 getChildContainer: function( )
31006 if (this.boxesEl) {
31007 return this.boxesEl;
31010 this.boxesEl = this.el.select('.mas-boxes').first();
31012 return this.boxesEl;
31016 initEvents : function()
31020 if(this.isAutoInitial){
31021 Roo.log('hook children rendered');
31022 this.on('childrenrendered', function() {
31023 Roo.log('children rendered');
31029 initial : function()
31031 this.selectedBrick = [];
31033 this.currentSize = this.el.getBox(true);
31035 Roo.EventManager.onWindowResize(this.resize, this);
31037 if(!this.isAutoInitial){
31045 //this.layout.defer(500,this);
31049 resize : function()
31051 var cs = this.el.getBox(true);
31054 this.currentSize.width == cs.width &&
31055 this.currentSize.x == cs.x &&
31056 this.currentSize.height == cs.height &&
31057 this.currentSize.y == cs.y
31059 Roo.log("no change in with or X or Y");
31063 this.currentSize = cs;
31069 layout : function()
31071 this._resetLayout();
31073 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31075 this.layoutItems( isInstant );
31077 this._isLayoutInited = true;
31079 this.fireEvent('layout', this);
31083 _resetLayout : function()
31085 if(this.isHorizontal){
31086 this.horizontalMeasureColumns();
31090 this.verticalMeasureColumns();
31094 verticalMeasureColumns : function()
31096 this.getContainerWidth();
31098 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31099 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31103 var boxWidth = this.boxWidth + this.padWidth;
31105 if(this.containerWidth < this.boxWidth){
31106 boxWidth = this.containerWidth
31109 var containerWidth = this.containerWidth;
31111 var cols = Math.floor(containerWidth / boxWidth);
31113 this.cols = Math.max( cols, 1 );
31115 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31117 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31119 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31121 this.colWidth = boxWidth + avail - this.padWidth;
31123 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31124 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31127 horizontalMeasureColumns : function()
31129 this.getContainerWidth();
31131 var boxWidth = this.boxWidth;
31133 if(this.containerWidth < boxWidth){
31134 boxWidth = this.containerWidth;
31137 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31139 this.el.setHeight(boxWidth);
31143 getContainerWidth : function()
31145 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31148 layoutItems : function( isInstant )
31150 Roo.log(this.bricks);
31152 var items = Roo.apply([], this.bricks);
31154 if(this.isHorizontal){
31155 this._horizontalLayoutItems( items , isInstant );
31159 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31160 // this._verticalAlternativeLayoutItems( items , isInstant );
31164 this._verticalLayoutItems( items , isInstant );
31168 _verticalLayoutItems : function ( items , isInstant)
31170 if ( !items || !items.length ) {
31175 ['xs', 'xs', 'xs', 'tall'],
31176 ['xs', 'xs', 'tall'],
31177 ['xs', 'xs', 'sm'],
31178 ['xs', 'xs', 'xs'],
31184 ['sm', 'xs', 'xs'],
31188 ['tall', 'xs', 'xs', 'xs'],
31189 ['tall', 'xs', 'xs'],
31201 Roo.each(items, function(item, k){
31203 switch (item.size) {
31204 // these layouts take up a full box,
31215 boxes.push([item]);
31238 var filterPattern = function(box, length)
31246 var pattern = box.slice(0, length);
31250 Roo.each(pattern, function(i){
31251 format.push(i.size);
31254 Roo.each(standard, function(s){
31256 if(String(s) != String(format)){
31265 if(!match && length == 1){
31270 filterPattern(box, length - 1);
31274 queue.push(pattern);
31276 box = box.slice(length, box.length);
31278 filterPattern(box, 4);
31284 Roo.each(boxes, function(box, k){
31290 if(box.length == 1){
31295 filterPattern(box, 4);
31299 this._processVerticalLayoutQueue( queue, isInstant );
31303 // _verticalAlternativeLayoutItems : function( items , isInstant )
31305 // if ( !items || !items.length ) {
31309 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31313 _horizontalLayoutItems : function ( items , isInstant)
31315 if ( !items || !items.length || items.length < 3) {
31321 var eItems = items.slice(0, 3);
31323 items = items.slice(3, items.length);
31326 ['xs', 'xs', 'xs', 'wide'],
31327 ['xs', 'xs', 'wide'],
31328 ['xs', 'xs', 'sm'],
31329 ['xs', 'xs', 'xs'],
31335 ['sm', 'xs', 'xs'],
31339 ['wide', 'xs', 'xs', 'xs'],
31340 ['wide', 'xs', 'xs'],
31353 Roo.each(items, function(item, k){
31355 switch (item.size) {
31366 boxes.push([item]);
31390 var filterPattern = function(box, length)
31398 var pattern = box.slice(0, length);
31402 Roo.each(pattern, function(i){
31403 format.push(i.size);
31406 Roo.each(standard, function(s){
31408 if(String(s) != String(format)){
31417 if(!match && length == 1){
31422 filterPattern(box, length - 1);
31426 queue.push(pattern);
31428 box = box.slice(length, box.length);
31430 filterPattern(box, 4);
31436 Roo.each(boxes, function(box, k){
31442 if(box.length == 1){
31447 filterPattern(box, 4);
31454 var pos = this.el.getBox(true);
31458 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31460 var hit_end = false;
31462 Roo.each(queue, function(box){
31466 Roo.each(box, function(b){
31468 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31478 Roo.each(box, function(b){
31480 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31483 mx = Math.max(mx, b.x);
31487 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31491 Roo.each(box, function(b){
31493 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31507 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31510 /** Sets position of item in DOM
31511 * @param {Element} item
31512 * @param {Number} x - horizontal position
31513 * @param {Number} y - vertical position
31514 * @param {Boolean} isInstant - disables transitions
31516 _processVerticalLayoutQueue : function( queue, isInstant )
31518 var pos = this.el.getBox(true);
31523 for (var i = 0; i < this.cols; i++){
31527 Roo.each(queue, function(box, k){
31529 var col = k % this.cols;
31531 Roo.each(box, function(b,kk){
31533 b.el.position('absolute');
31535 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31536 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31538 if(b.size == 'md-left' || b.size == 'md-right'){
31539 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31540 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31543 b.el.setWidth(width);
31544 b.el.setHeight(height);
31546 b.el.select('iframe',true).setSize(width,height);
31550 for (var i = 0; i < this.cols; i++){
31552 if(maxY[i] < maxY[col]){
31557 col = Math.min(col, i);
31561 x = pos.x + col * (this.colWidth + this.padWidth);
31565 var positions = [];
31567 switch (box.length){
31569 positions = this.getVerticalOneBoxColPositions(x, y, box);
31572 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31575 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31578 positions = this.getVerticalFourBoxColPositions(x, y, box);
31584 Roo.each(box, function(b,kk){
31586 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31588 var sz = b.el.getSize();
31590 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31598 for (var i = 0; i < this.cols; i++){
31599 mY = Math.max(mY, maxY[i]);
31602 this.el.setHeight(mY - pos.y);
31606 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31608 // var pos = this.el.getBox(true);
31611 // var maxX = pos.right;
31613 // var maxHeight = 0;
31615 // Roo.each(items, function(item, k){
31619 // item.el.position('absolute');
31621 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31623 // item.el.setWidth(width);
31625 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31627 // item.el.setHeight(height);
31630 // item.el.setXY([x, y], isInstant ? false : true);
31632 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31635 // y = y + height + this.alternativePadWidth;
31637 // maxHeight = maxHeight + height + this.alternativePadWidth;
31641 // this.el.setHeight(maxHeight);
31645 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31647 var pos = this.el.getBox(true);
31652 var maxX = pos.right;
31654 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31656 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31658 Roo.each(queue, function(box, k){
31660 Roo.each(box, function(b, kk){
31662 b.el.position('absolute');
31664 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31665 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31667 if(b.size == 'md-left' || b.size == 'md-right'){
31668 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31669 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31672 b.el.setWidth(width);
31673 b.el.setHeight(height);
31681 var positions = [];
31683 switch (box.length){
31685 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31688 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31691 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31694 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31700 Roo.each(box, function(b,kk){
31702 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31704 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31712 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31714 Roo.each(eItems, function(b,k){
31716 b.size = (k == 0) ? 'sm' : 'xs';
31717 b.x = (k == 0) ? 2 : 1;
31718 b.y = (k == 0) ? 2 : 1;
31720 b.el.position('absolute');
31722 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31724 b.el.setWidth(width);
31726 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31728 b.el.setHeight(height);
31732 var positions = [];
31735 x : maxX - this.unitWidth * 2 - this.gutter,
31740 x : maxX - this.unitWidth,
31741 y : minY + (this.unitWidth + this.gutter) * 2
31745 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31749 Roo.each(eItems, function(b,k){
31751 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31757 getVerticalOneBoxColPositions : function(x, y, box)
31761 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31763 if(box[0].size == 'md-left'){
31767 if(box[0].size == 'md-right'){
31772 x : x + (this.unitWidth + this.gutter) * rand,
31779 getVerticalTwoBoxColPositions : function(x, y, box)
31783 if(box[0].size == 'xs'){
31787 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31791 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31805 x : x + (this.unitWidth + this.gutter) * 2,
31806 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31813 getVerticalThreeBoxColPositions : function(x, y, box)
31817 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31825 x : x + (this.unitWidth + this.gutter) * 1,
31830 x : x + (this.unitWidth + this.gutter) * 2,
31838 if(box[0].size == 'xs' && box[1].size == 'xs'){
31847 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31851 x : x + (this.unitWidth + this.gutter) * 1,
31865 x : x + (this.unitWidth + this.gutter) * 2,
31870 x : x + (this.unitWidth + this.gutter) * 2,
31871 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31878 getVerticalFourBoxColPositions : function(x, y, box)
31882 if(box[0].size == 'xs'){
31891 y : y + (this.unitHeight + this.gutter) * 1
31896 y : y + (this.unitHeight + this.gutter) * 2
31900 x : x + (this.unitWidth + this.gutter) * 1,
31914 x : x + (this.unitWidth + this.gutter) * 2,
31919 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31920 y : y + (this.unitHeight + this.gutter) * 1
31924 x : x + (this.unitWidth + this.gutter) * 2,
31925 y : y + (this.unitWidth + this.gutter) * 2
31932 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31936 if(box[0].size == 'md-left'){
31938 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31945 if(box[0].size == 'md-right'){
31947 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31948 y : minY + (this.unitWidth + this.gutter) * 1
31954 var rand = Math.floor(Math.random() * (4 - box[0].y));
31957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31958 y : minY + (this.unitWidth + this.gutter) * rand
31965 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31969 if(box[0].size == 'xs'){
31972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31991 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31992 y : minY + (this.unitWidth + this.gutter) * 2
31999 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32003 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32006 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32011 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32012 y : minY + (this.unitWidth + this.gutter) * 1
32016 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32017 y : minY + (this.unitWidth + this.gutter) * 2
32024 if(box[0].size == 'xs' && box[1].size == 'xs'){
32027 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32037 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32038 y : minY + (this.unitWidth + this.gutter) * 1
32046 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32051 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32052 y : minY + (this.unitWidth + this.gutter) * 2
32056 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32057 y : minY + (this.unitWidth + this.gutter) * 2
32064 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32068 if(box[0].size == 'xs'){
32071 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32076 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32081 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),
32086 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32087 y : minY + (this.unitWidth + this.gutter) * 1
32095 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32100 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32101 y : minY + (this.unitWidth + this.gutter) * 2
32105 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32106 y : minY + (this.unitWidth + this.gutter) * 2
32110 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),
32111 y : minY + (this.unitWidth + this.gutter) * 2
32119 * remove a Masonry Brick
32120 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32122 removeBrick : function(brick_id)
32128 for (var i = 0; i<this.bricks.length; i++) {
32129 if (this.bricks[i].id == brick_id) {
32130 this.bricks.splice(i,1);
32131 this.el.dom.removeChild(Roo.get(brick_id).dom);
32138 * adds a Masonry Brick
32139 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32141 addBrick : function(cfg)
32143 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32144 //this.register(cn);
32145 cn.parentId = this.id;
32146 cn.render(this.el);
32151 * register a Masonry Brick
32152 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32155 register : function(brick)
32157 this.bricks.push(brick);
32158 brick.masonryId = this.id;
32162 * clear all the Masonry Brick
32164 clearAll : function()
32167 //this.getChildContainer().dom.innerHTML = "";
32168 this.el.dom.innerHTML = '';
32171 getSelected : function()
32173 if (!this.selectedBrick) {
32177 return this.selectedBrick;
32181 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32185 * register a Masonry Layout
32186 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32189 register : function(layout)
32191 this.groups[layout.id] = layout;
32194 * fetch a Masonry Layout based on the masonry layout ID
32195 * @param {string} the masonry layout to add
32196 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32199 get: function(layout_id) {
32200 if (typeof(this.groups[layout_id]) == 'undefined') {
32203 return this.groups[layout_id] ;
32215 * http://masonry.desandro.com
32217 * The idea is to render all the bricks based on vertical width...
32219 * The original code extends 'outlayer' - we might need to use that....
32225 * @class Roo.bootstrap.LayoutMasonryAuto
32226 * @extends Roo.bootstrap.Component
32227 * Bootstrap Layout Masonry class
32230 * Create a new Element
32231 * @param {Object} config The config object
32234 Roo.bootstrap.LayoutMasonryAuto = function(config){
32235 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32238 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32241 * @cfg {Boolean} isFitWidth - resize the width..
32243 isFitWidth : false, // options..
32245 * @cfg {Boolean} isOriginLeft = left align?
32247 isOriginLeft : true,
32249 * @cfg {Boolean} isOriginTop = top align?
32251 isOriginTop : false,
32253 * @cfg {Boolean} isLayoutInstant = no animation?
32255 isLayoutInstant : false, // needed?
32257 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32259 isResizingContainer : true,
32261 * @cfg {Number} columnWidth width of the columns
32267 * @cfg {Number} maxCols maximum number of columns
32272 * @cfg {Number} padHeight padding below box..
32278 * @cfg {Boolean} isAutoInitial defalut true
32281 isAutoInitial : true,
32287 initialColumnWidth : 0,
32288 currentSize : null,
32290 colYs : null, // array.
32297 bricks: null, //CompositeElement
32298 cols : 0, // array?
32299 // element : null, // wrapped now this.el
32300 _isLayoutInited : null,
32303 getAutoCreate : function(){
32307 cls: 'blog-masonary-wrapper ' + this.cls,
32309 cls : 'mas-boxes masonary'
32316 getChildContainer: function( )
32318 if (this.boxesEl) {
32319 return this.boxesEl;
32322 this.boxesEl = this.el.select('.mas-boxes').first();
32324 return this.boxesEl;
32328 initEvents : function()
32332 if(this.isAutoInitial){
32333 Roo.log('hook children rendered');
32334 this.on('childrenrendered', function() {
32335 Roo.log('children rendered');
32342 initial : function()
32344 this.reloadItems();
32346 this.currentSize = this.el.getBox(true);
32348 /// was window resize... - let's see if this works..
32349 Roo.EventManager.onWindowResize(this.resize, this);
32351 if(!this.isAutoInitial){
32356 this.layout.defer(500,this);
32359 reloadItems: function()
32361 this.bricks = this.el.select('.masonry-brick', true);
32363 this.bricks.each(function(b) {
32364 //Roo.log(b.getSize());
32365 if (!b.attr('originalwidth')) {
32366 b.attr('originalwidth', b.getSize().width);
32371 Roo.log(this.bricks.elements.length);
32374 resize : function()
32377 var cs = this.el.getBox(true);
32379 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32380 Roo.log("no change in with or X");
32383 this.currentSize = cs;
32387 layout : function()
32390 this._resetLayout();
32391 //this._manageStamps();
32393 // don't animate first layout
32394 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32395 this.layoutItems( isInstant );
32397 // flag for initalized
32398 this._isLayoutInited = true;
32401 layoutItems : function( isInstant )
32403 //var items = this._getItemsForLayout( this.items );
32404 // original code supports filtering layout items.. we just ignore it..
32406 this._layoutItems( this.bricks , isInstant );
32408 this._postLayout();
32410 _layoutItems : function ( items , isInstant)
32412 //this.fireEvent( 'layout', this, items );
32415 if ( !items || !items.elements.length ) {
32416 // no items, emit event with empty array
32421 items.each(function(item) {
32422 Roo.log("layout item");
32424 // get x/y object from method
32425 var position = this._getItemLayoutPosition( item );
32427 position.item = item;
32428 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32429 queue.push( position );
32432 this._processLayoutQueue( queue );
32434 /** Sets position of item in DOM
32435 * @param {Element} item
32436 * @param {Number} x - horizontal position
32437 * @param {Number} y - vertical position
32438 * @param {Boolean} isInstant - disables transitions
32440 _processLayoutQueue : function( queue )
32442 for ( var i=0, len = queue.length; i < len; i++ ) {
32443 var obj = queue[i];
32444 obj.item.position('absolute');
32445 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32451 * Any logic you want to do after each layout,
32452 * i.e. size the container
32454 _postLayout : function()
32456 this.resizeContainer();
32459 resizeContainer : function()
32461 if ( !this.isResizingContainer ) {
32464 var size = this._getContainerSize();
32466 this.el.setSize(size.width,size.height);
32467 this.boxesEl.setSize(size.width,size.height);
32473 _resetLayout : function()
32475 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32476 this.colWidth = this.el.getWidth();
32477 //this.gutter = this.el.getWidth();
32479 this.measureColumns();
32485 this.colYs.push( 0 );
32491 measureColumns : function()
32493 this.getContainerWidth();
32494 // if columnWidth is 0, default to outerWidth of first item
32495 if ( !this.columnWidth ) {
32496 var firstItem = this.bricks.first();
32497 Roo.log(firstItem);
32498 this.columnWidth = this.containerWidth;
32499 if (firstItem && firstItem.attr('originalwidth') ) {
32500 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32502 // columnWidth fall back to item of first element
32503 Roo.log("set column width?");
32504 this.initialColumnWidth = this.columnWidth ;
32506 // if first elem has no width, default to size of container
32511 if (this.initialColumnWidth) {
32512 this.columnWidth = this.initialColumnWidth;
32517 // column width is fixed at the top - however if container width get's smaller we should
32520 // this bit calcs how man columns..
32522 var columnWidth = this.columnWidth += this.gutter;
32524 // calculate columns
32525 var containerWidth = this.containerWidth + this.gutter;
32527 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32528 // fix rounding errors, typically with gutters
32529 var excess = columnWidth - containerWidth % columnWidth;
32532 // if overshoot is less than a pixel, round up, otherwise floor it
32533 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32534 cols = Math[ mathMethod ]( cols );
32535 this.cols = Math.max( cols, 1 );
32536 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32538 // padding positioning..
32539 var totalColWidth = this.cols * this.columnWidth;
32540 var padavail = this.containerWidth - totalColWidth;
32541 // so for 2 columns - we need 3 'pads'
32543 var padNeeded = (1+this.cols) * this.padWidth;
32545 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32547 this.columnWidth += padExtra
32548 //this.padWidth = Math.floor(padavail / ( this.cols));
32550 // adjust colum width so that padding is fixed??
32552 // we have 3 columns ... total = width * 3
32553 // we have X left over... that should be used by
32555 //if (this.expandC) {
32563 getContainerWidth : function()
32565 /* // container is parent if fit width
32566 var container = this.isFitWidth ? this.element.parentNode : this.element;
32567 // check that this.size and size are there
32568 // IE8 triggers resize on body size change, so they might not be
32570 var size = getSize( container ); //FIXME
32571 this.containerWidth = size && size.innerWidth; //FIXME
32574 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32578 _getItemLayoutPosition : function( item ) // what is item?
32580 // we resize the item to our columnWidth..
32582 item.setWidth(this.columnWidth);
32583 item.autoBoxAdjust = false;
32585 var sz = item.getSize();
32587 // how many columns does this brick span
32588 var remainder = this.containerWidth % this.columnWidth;
32590 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32591 // round if off by 1 pixel, otherwise use ceil
32592 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32593 colSpan = Math.min( colSpan, this.cols );
32595 // normally this should be '1' as we dont' currently allow multi width columns..
32597 var colGroup = this._getColGroup( colSpan );
32598 // get the minimum Y value from the columns
32599 var minimumY = Math.min.apply( Math, colGroup );
32600 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32602 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32604 // position the brick
32606 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32607 y: this.currentSize.y + minimumY + this.padHeight
32611 // apply setHeight to necessary columns
32612 var setHeight = minimumY + sz.height + this.padHeight;
32613 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32615 var setSpan = this.cols + 1 - colGroup.length;
32616 for ( var i = 0; i < setSpan; i++ ) {
32617 this.colYs[ shortColIndex + i ] = setHeight ;
32624 * @param {Number} colSpan - number of columns the element spans
32625 * @returns {Array} colGroup
32627 _getColGroup : function( colSpan )
32629 if ( colSpan < 2 ) {
32630 // if brick spans only one column, use all the column Ys
32635 // how many different places could this brick fit horizontally
32636 var groupCount = this.cols + 1 - colSpan;
32637 // for each group potential horizontal position
32638 for ( var i = 0; i < groupCount; i++ ) {
32639 // make an array of colY values for that one group
32640 var groupColYs = this.colYs.slice( i, i + colSpan );
32641 // and get the max value of the array
32642 colGroup[i] = Math.max.apply( Math, groupColYs );
32647 _manageStamp : function( stamp )
32649 var stampSize = stamp.getSize();
32650 var offset = stamp.getBox();
32651 // get the columns that this stamp affects
32652 var firstX = this.isOriginLeft ? offset.x : offset.right;
32653 var lastX = firstX + stampSize.width;
32654 var firstCol = Math.floor( firstX / this.columnWidth );
32655 firstCol = Math.max( 0, firstCol );
32657 var lastCol = Math.floor( lastX / this.columnWidth );
32658 // lastCol should not go over if multiple of columnWidth #425
32659 lastCol -= lastX % this.columnWidth ? 0 : 1;
32660 lastCol = Math.min( this.cols - 1, lastCol );
32662 // set colYs to bottom of the stamp
32663 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32666 for ( var i = firstCol; i <= lastCol; i++ ) {
32667 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32672 _getContainerSize : function()
32674 this.maxY = Math.max.apply( Math, this.colYs );
32679 if ( this.isFitWidth ) {
32680 size.width = this._getContainerFitWidth();
32686 _getContainerFitWidth : function()
32688 var unusedCols = 0;
32689 // count unused columns
32692 if ( this.colYs[i] !== 0 ) {
32697 // fit container to columns that have been used
32698 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32701 needsResizeLayout : function()
32703 var previousWidth = this.containerWidth;
32704 this.getContainerWidth();
32705 return previousWidth !== this.containerWidth;
32720 * @class Roo.bootstrap.MasonryBrick
32721 * @extends Roo.bootstrap.Component
32722 * Bootstrap MasonryBrick class
32725 * Create a new MasonryBrick
32726 * @param {Object} config The config object
32729 Roo.bootstrap.MasonryBrick = function(config){
32731 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32733 Roo.bootstrap.MasonryBrick.register(this);
32739 * When a MasonryBrick is clcik
32740 * @param {Roo.bootstrap.MasonryBrick} this
32741 * @param {Roo.EventObject} e
32747 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32750 * @cfg {String} title
32754 * @cfg {String} html
32758 * @cfg {String} bgimage
32762 * @cfg {String} videourl
32766 * @cfg {String} cls
32770 * @cfg {String} href
32774 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32779 * @cfg {String} placetitle (center|bottom)
32784 * @cfg {Boolean} isFitContainer defalut true
32786 isFitContainer : true,
32789 * @cfg {Boolean} preventDefault defalut false
32791 preventDefault : false,
32794 * @cfg {Boolean} inverse defalut false
32796 maskInverse : false,
32798 getAutoCreate : function()
32800 if(!this.isFitContainer){
32801 return this.getSplitAutoCreate();
32804 var cls = 'masonry-brick masonry-brick-full';
32806 if(this.href.length){
32807 cls += ' masonry-brick-link';
32810 if(this.bgimage.length){
32811 cls += ' masonry-brick-image';
32814 if(this.maskInverse){
32815 cls += ' mask-inverse';
32818 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32819 cls += ' enable-mask';
32823 cls += ' masonry-' + this.size + '-brick';
32826 if(this.placetitle.length){
32828 switch (this.placetitle) {
32830 cls += ' masonry-center-title';
32833 cls += ' masonry-bottom-title';
32840 if(!this.html.length && !this.bgimage.length){
32841 cls += ' masonry-center-title';
32844 if(!this.html.length && this.bgimage.length){
32845 cls += ' masonry-bottom-title';
32850 cls += ' ' + this.cls;
32854 tag: (this.href.length) ? 'a' : 'div',
32859 cls: 'masonry-brick-mask'
32863 cls: 'masonry-brick-paragraph',
32869 if(this.href.length){
32870 cfg.href = this.href;
32873 var cn = cfg.cn[1].cn;
32875 if(this.title.length){
32878 cls: 'masonry-brick-title',
32883 if(this.html.length){
32886 cls: 'masonry-brick-text',
32891 if (!this.title.length && !this.html.length) {
32892 cfg.cn[1].cls += ' hide';
32895 if(this.bgimage.length){
32898 cls: 'masonry-brick-image-view',
32903 if(this.videourl.length){
32904 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32905 // youtube support only?
32908 cls: 'masonry-brick-image-view',
32911 allowfullscreen : true
32919 getSplitAutoCreate : function()
32921 var cls = 'masonry-brick masonry-brick-split';
32923 if(this.href.length){
32924 cls += ' masonry-brick-link';
32927 if(this.bgimage.length){
32928 cls += ' masonry-brick-image';
32932 cls += ' masonry-' + this.size + '-brick';
32935 switch (this.placetitle) {
32937 cls += ' masonry-center-title';
32940 cls += ' masonry-bottom-title';
32943 if(!this.bgimage.length){
32944 cls += ' masonry-center-title';
32947 if(this.bgimage.length){
32948 cls += ' masonry-bottom-title';
32954 cls += ' ' + this.cls;
32958 tag: (this.href.length) ? 'a' : 'div',
32963 cls: 'masonry-brick-split-head',
32967 cls: 'masonry-brick-paragraph',
32974 cls: 'masonry-brick-split-body',
32980 if(this.href.length){
32981 cfg.href = this.href;
32984 if(this.title.length){
32985 cfg.cn[0].cn[0].cn.push({
32987 cls: 'masonry-brick-title',
32992 if(this.html.length){
32993 cfg.cn[1].cn.push({
32995 cls: 'masonry-brick-text',
33000 if(this.bgimage.length){
33001 cfg.cn[0].cn.push({
33003 cls: 'masonry-brick-image-view',
33008 if(this.videourl.length){
33009 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33010 // youtube support only?
33011 cfg.cn[0].cn.cn.push({
33013 cls: 'masonry-brick-image-view',
33016 allowfullscreen : true
33023 initEvents: function()
33025 switch (this.size) {
33058 this.el.on('touchstart', this.onTouchStart, this);
33059 this.el.on('touchmove', this.onTouchMove, this);
33060 this.el.on('touchend', this.onTouchEnd, this);
33061 this.el.on('contextmenu', this.onContextMenu, this);
33063 this.el.on('mouseenter' ,this.enter, this);
33064 this.el.on('mouseleave', this.leave, this);
33065 this.el.on('click', this.onClick, this);
33068 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33069 this.parent().bricks.push(this);
33074 onClick: function(e, el)
33076 var time = this.endTimer - this.startTimer;
33077 // Roo.log(e.preventDefault());
33080 e.preventDefault();
33085 if(!this.preventDefault){
33089 e.preventDefault();
33091 if (this.activeClass != '') {
33092 this.selectBrick();
33095 this.fireEvent('click', this, e);
33098 enter: function(e, el)
33100 e.preventDefault();
33102 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33106 if(this.bgimage.length && this.html.length){
33107 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33111 leave: function(e, el)
33113 e.preventDefault();
33115 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33119 if(this.bgimage.length && this.html.length){
33120 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33124 onTouchStart: function(e, el)
33126 // e.preventDefault();
33128 this.touchmoved = false;
33130 if(!this.isFitContainer){
33134 if(!this.bgimage.length || !this.html.length){
33138 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33140 this.timer = new Date().getTime();
33144 onTouchMove: function(e, el)
33146 this.touchmoved = true;
33149 onContextMenu : function(e,el)
33151 e.preventDefault();
33152 e.stopPropagation();
33156 onTouchEnd: function(e, el)
33158 // e.preventDefault();
33160 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33167 if(!this.bgimage.length || !this.html.length){
33169 if(this.href.length){
33170 window.location.href = this.href;
33176 if(!this.isFitContainer){
33180 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33182 window.location.href = this.href;
33185 //selection on single brick only
33186 selectBrick : function() {
33188 if (!this.parentId) {
33192 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33193 var index = m.selectedBrick.indexOf(this.id);
33196 m.selectedBrick.splice(index,1);
33197 this.el.removeClass(this.activeClass);
33201 for(var i = 0; i < m.selectedBrick.length; i++) {
33202 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33203 b.el.removeClass(b.activeClass);
33206 m.selectedBrick = [];
33208 m.selectedBrick.push(this.id);
33209 this.el.addClass(this.activeClass);
33213 isSelected : function(){
33214 return this.el.hasClass(this.activeClass);
33219 Roo.apply(Roo.bootstrap.MasonryBrick, {
33222 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33224 * register a Masonry Brick
33225 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33228 register : function(brick)
33230 //this.groups[brick.id] = brick;
33231 this.groups.add(brick.id, brick);
33234 * fetch a masonry brick based on the masonry brick ID
33235 * @param {string} the masonry brick to add
33236 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33239 get: function(brick_id)
33241 // if (typeof(this.groups[brick_id]) == 'undefined') {
33244 // return this.groups[brick_id] ;
33246 if(this.groups.key(brick_id)) {
33247 return this.groups.key(brick_id);
33265 * @class Roo.bootstrap.Brick
33266 * @extends Roo.bootstrap.Component
33267 * Bootstrap Brick class
33270 * Create a new Brick
33271 * @param {Object} config The config object
33274 Roo.bootstrap.Brick = function(config){
33275 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33281 * When a Brick is click
33282 * @param {Roo.bootstrap.Brick} this
33283 * @param {Roo.EventObject} e
33289 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33292 * @cfg {String} title
33296 * @cfg {String} html
33300 * @cfg {String} bgimage
33304 * @cfg {String} cls
33308 * @cfg {String} href
33312 * @cfg {String} video
33316 * @cfg {Boolean} square
33320 getAutoCreate : function()
33322 var cls = 'roo-brick';
33324 if(this.href.length){
33325 cls += ' roo-brick-link';
33328 if(this.bgimage.length){
33329 cls += ' roo-brick-image';
33332 if(!this.html.length && !this.bgimage.length){
33333 cls += ' roo-brick-center-title';
33336 if(!this.html.length && this.bgimage.length){
33337 cls += ' roo-brick-bottom-title';
33341 cls += ' ' + this.cls;
33345 tag: (this.href.length) ? 'a' : 'div',
33350 cls: 'roo-brick-paragraph',
33356 if(this.href.length){
33357 cfg.href = this.href;
33360 var cn = cfg.cn[0].cn;
33362 if(this.title.length){
33365 cls: 'roo-brick-title',
33370 if(this.html.length){
33373 cls: 'roo-brick-text',
33380 if(this.bgimage.length){
33383 cls: 'roo-brick-image-view',
33391 initEvents: function()
33393 if(this.title.length || this.html.length){
33394 this.el.on('mouseenter' ,this.enter, this);
33395 this.el.on('mouseleave', this.leave, this);
33398 Roo.EventManager.onWindowResize(this.resize, this);
33400 if(this.bgimage.length){
33401 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33402 this.imageEl.on('load', this.onImageLoad, this);
33409 onImageLoad : function()
33414 resize : function()
33416 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33418 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33420 if(this.bgimage.length){
33421 var image = this.el.select('.roo-brick-image-view', true).first();
33423 image.setWidth(paragraph.getWidth());
33426 image.setHeight(paragraph.getWidth());
33429 this.el.setHeight(image.getHeight());
33430 paragraph.setHeight(image.getHeight());
33436 enter: function(e, el)
33438 e.preventDefault();
33440 if(this.bgimage.length){
33441 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33442 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33446 leave: function(e, el)
33448 e.preventDefault();
33450 if(this.bgimage.length){
33451 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33452 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33467 * @class Roo.bootstrap.NumberField
33468 * @extends Roo.bootstrap.Input
33469 * Bootstrap NumberField class
33475 * Create a new NumberField
33476 * @param {Object} config The config object
33479 Roo.bootstrap.NumberField = function(config){
33480 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33483 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33486 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33488 allowDecimals : true,
33490 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33492 decimalSeparator : ".",
33494 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33496 decimalPrecision : 2,
33498 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33500 allowNegative : true,
33503 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33507 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33509 minValue : Number.NEGATIVE_INFINITY,
33511 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33513 maxValue : Number.MAX_VALUE,
33515 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33517 minText : "The minimum value for this field is {0}",
33519 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33521 maxText : "The maximum value for this field is {0}",
33523 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33524 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33526 nanText : "{0} is not a valid number",
33528 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33530 thousandsDelimiter : false,
33532 * @cfg {String} valueAlign alignment of value
33534 valueAlign : "left",
33536 getAutoCreate : function()
33538 var hiddenInput = {
33542 cls: 'hidden-number-input'
33546 hiddenInput.name = this.name;
33551 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33553 this.name = hiddenInput.name;
33555 if(cfg.cn.length > 0) {
33556 cfg.cn.push(hiddenInput);
33563 initEvents : function()
33565 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33567 var allowed = "0123456789";
33569 if(this.allowDecimals){
33570 allowed += this.decimalSeparator;
33573 if(this.allowNegative){
33577 if(this.thousandsDelimiter) {
33581 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33583 var keyPress = function(e){
33585 var k = e.getKey();
33587 var c = e.getCharCode();
33590 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33591 allowed.indexOf(String.fromCharCode(c)) === -1
33597 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33601 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33606 this.el.on("keypress", keyPress, this);
33609 validateValue : function(value)
33612 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33616 var num = this.parseValue(value);
33619 this.markInvalid(String.format(this.nanText, value));
33623 if(num < this.minValue){
33624 this.markInvalid(String.format(this.minText, this.minValue));
33628 if(num > this.maxValue){
33629 this.markInvalid(String.format(this.maxText, this.maxValue));
33636 getValue : function()
33638 var v = this.hiddenEl().getValue();
33640 return this.fixPrecision(this.parseValue(v));
33643 parseValue : function(value)
33645 if(this.thousandsDelimiter) {
33647 r = new RegExp(",", "g");
33648 value = value.replace(r, "");
33651 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33652 return isNaN(value) ? '' : value;
33655 fixPrecision : function(value)
33657 if(this.thousandsDelimiter) {
33659 r = new RegExp(",", "g");
33660 value = value.replace(r, "");
33663 var nan = isNaN(value);
33665 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33666 return nan ? '' : value;
33668 return parseFloat(value).toFixed(this.decimalPrecision);
33671 setValue : function(v)
33673 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33679 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33681 this.inputEl().dom.value = (v == '') ? '' :
33682 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33684 if(!this.allowZero && v === '0') {
33685 this.hiddenEl().dom.value = '';
33686 this.inputEl().dom.value = '';
33693 decimalPrecisionFcn : function(v)
33695 return Math.floor(v);
33698 beforeBlur : function()
33700 var v = this.parseValue(this.getRawValue());
33702 if(v || v === 0 || v === ''){
33707 hiddenEl : function()
33709 return this.el.select('input.hidden-number-input',true).first();
33721 * @class Roo.bootstrap.DocumentSlider
33722 * @extends Roo.bootstrap.Component
33723 * Bootstrap DocumentSlider class
33726 * Create a new DocumentViewer
33727 * @param {Object} config The config object
33730 Roo.bootstrap.DocumentSlider = function(config){
33731 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33738 * Fire after initEvent
33739 * @param {Roo.bootstrap.DocumentSlider} this
33744 * Fire after update
33745 * @param {Roo.bootstrap.DocumentSlider} this
33751 * @param {Roo.bootstrap.DocumentSlider} this
33757 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33763 getAutoCreate : function()
33767 cls : 'roo-document-slider',
33771 cls : 'roo-document-slider-header',
33775 cls : 'roo-document-slider-header-title'
33781 cls : 'roo-document-slider-body',
33785 cls : 'roo-document-slider-prev',
33789 cls : 'fa fa-chevron-left'
33795 cls : 'roo-document-slider-thumb',
33799 cls : 'roo-document-slider-image'
33805 cls : 'roo-document-slider-next',
33809 cls : 'fa fa-chevron-right'
33821 initEvents : function()
33823 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33824 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33826 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33827 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33829 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33830 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33832 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33833 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33835 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33836 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33838 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33839 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33841 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33842 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33844 this.thumbEl.on('click', this.onClick, this);
33846 this.prevIndicator.on('click', this.prev, this);
33848 this.nextIndicator.on('click', this.next, this);
33852 initial : function()
33854 if(this.files.length){
33855 this.indicator = 1;
33859 this.fireEvent('initial', this);
33862 update : function()
33864 this.imageEl.attr('src', this.files[this.indicator - 1]);
33866 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33868 this.prevIndicator.show();
33870 if(this.indicator == 1){
33871 this.prevIndicator.hide();
33874 this.nextIndicator.show();
33876 if(this.indicator == this.files.length){
33877 this.nextIndicator.hide();
33880 this.thumbEl.scrollTo('top');
33882 this.fireEvent('update', this);
33885 onClick : function(e)
33887 e.preventDefault();
33889 this.fireEvent('click', this);
33894 e.preventDefault();
33896 this.indicator = Math.max(1, this.indicator - 1);
33903 e.preventDefault();
33905 this.indicator = Math.min(this.files.length, this.indicator + 1);
33919 * @class Roo.bootstrap.RadioSet
33920 * @extends Roo.bootstrap.Input
33921 * Bootstrap RadioSet class
33922 * @cfg {String} indicatorpos (left|right) default left
33923 * @cfg {Boolean} inline (true|false) inline the element (default true)
33924 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33926 * Create a new RadioSet
33927 * @param {Object} config The config object
33930 Roo.bootstrap.RadioSet = function(config){
33932 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33936 Roo.bootstrap.RadioSet.register(this);
33941 * Fires when the element is checked or unchecked.
33942 * @param {Roo.bootstrap.RadioSet} this This radio
33943 * @param {Roo.bootstrap.Radio} item The checked item
33948 * Fires when the element is click.
33949 * @param {Roo.bootstrap.RadioSet} this This radio set
33950 * @param {Roo.bootstrap.Radio} item The checked item
33951 * @param {Roo.EventObject} e The event object
33958 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33966 indicatorpos : 'left',
33968 getAutoCreate : function()
33972 cls : 'roo-radio-set-label',
33976 html : this.fieldLabel
33981 if(this.indicatorpos == 'left'){
33984 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33985 tooltip : 'This field is required'
33990 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33991 tooltip : 'This field is required'
33997 cls : 'roo-radio-set-items'
34000 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34002 if (align === 'left' && this.fieldLabel.length) {
34005 cls : "roo-radio-set-right",
34011 if(this.labelWidth > 12){
34012 label.style = "width: " + this.labelWidth + 'px';
34015 if(this.labelWidth < 13 && this.labelmd == 0){
34016 this.labelmd = this.labelWidth;
34019 if(this.labellg > 0){
34020 label.cls += ' col-lg-' + this.labellg;
34021 items.cls += ' col-lg-' + (12 - this.labellg);
34024 if(this.labelmd > 0){
34025 label.cls += ' col-md-' + this.labelmd;
34026 items.cls += ' col-md-' + (12 - this.labelmd);
34029 if(this.labelsm > 0){
34030 label.cls += ' col-sm-' + this.labelsm;
34031 items.cls += ' col-sm-' + (12 - this.labelsm);
34034 if(this.labelxs > 0){
34035 label.cls += ' col-xs-' + this.labelxs;
34036 items.cls += ' col-xs-' + (12 - this.labelxs);
34042 cls : 'roo-radio-set',
34046 cls : 'roo-radio-set-input',
34049 value : this.value ? this.value : ''
34056 if(this.weight.length){
34057 cfg.cls += ' roo-radio-' + this.weight;
34061 cfg.cls += ' roo-radio-set-inline';
34065 ['xs','sm','md','lg'].map(function(size){
34066 if (settings[size]) {
34067 cfg.cls += ' col-' + size + '-' + settings[size];
34075 initEvents : function()
34077 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34078 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34080 if(!this.fieldLabel.length){
34081 this.labelEl.hide();
34084 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34085 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34087 this.indicator = this.indicatorEl();
34089 if(this.indicator){
34090 this.indicator.addClass('invisible');
34093 this.originalValue = this.getValue();
34097 inputEl: function ()
34099 return this.el.select('.roo-radio-set-input', true).first();
34102 getChildContainer : function()
34104 return this.itemsEl;
34107 register : function(item)
34109 this.radioes.push(item);
34113 validate : function()
34115 if(this.getVisibilityEl().hasClass('hidden')){
34121 Roo.each(this.radioes, function(i){
34130 if(this.allowBlank) {
34134 if(this.disabled || valid){
34139 this.markInvalid();
34144 markValid : function()
34146 if(this.labelEl.isVisible(true)){
34147 this.indicatorEl().removeClass('visible');
34148 this.indicatorEl().addClass('invisible');
34151 this.el.removeClass([this.invalidClass, this.validClass]);
34152 this.el.addClass(this.validClass);
34154 this.fireEvent('valid', this);
34157 markInvalid : function(msg)
34159 if(this.allowBlank || this.disabled){
34163 if(this.labelEl.isVisible(true)){
34164 this.indicatorEl().removeClass('invisible');
34165 this.indicatorEl().addClass('visible');
34168 this.el.removeClass([this.invalidClass, this.validClass]);
34169 this.el.addClass(this.invalidClass);
34171 this.fireEvent('invalid', this, msg);
34175 setValue : function(v, suppressEvent)
34177 if(this.value === v){
34184 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34187 Roo.each(this.radioes, function(i){
34189 i.el.removeClass('checked');
34192 Roo.each(this.radioes, function(i){
34194 if(i.value === v || i.value.toString() === v.toString()){
34196 i.el.addClass('checked');
34198 if(suppressEvent !== true){
34199 this.fireEvent('check', this, i);
34210 clearInvalid : function(){
34212 if(!this.el || this.preventMark){
34216 this.el.removeClass([this.invalidClass]);
34218 this.fireEvent('valid', this);
34223 Roo.apply(Roo.bootstrap.RadioSet, {
34227 register : function(set)
34229 this.groups[set.name] = set;
34232 get: function(name)
34234 if (typeof(this.groups[name]) == 'undefined') {
34238 return this.groups[name] ;
34244 * Ext JS Library 1.1.1
34245 * Copyright(c) 2006-2007, Ext JS, LLC.
34247 * Originally Released Under LGPL - original licence link has changed is not relivant.
34250 * <script type="text/javascript">
34255 * @class Roo.bootstrap.SplitBar
34256 * @extends Roo.util.Observable
34257 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34261 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34262 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34263 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34264 split.minSize = 100;
34265 split.maxSize = 600;
34266 split.animate = true;
34267 split.on('moved', splitterMoved);
34270 * Create a new SplitBar
34271 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34272 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34273 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34274 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34275 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34276 position of the SplitBar).
34278 Roo.bootstrap.SplitBar = function(cfg){
34283 // dragElement : elm
34284 // resizingElement: el,
34286 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34287 // placement : Roo.bootstrap.SplitBar.LEFT ,
34288 // existingProxy ???
34291 this.el = Roo.get(cfg.dragElement, true);
34292 this.el.dom.unselectable = "on";
34294 this.resizingEl = Roo.get(cfg.resizingElement, true);
34298 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34299 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34302 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34305 * The minimum size of the resizing element. (Defaults to 0)
34311 * The maximum size of the resizing element. (Defaults to 2000)
34314 this.maxSize = 2000;
34317 * Whether to animate the transition to the new size
34320 this.animate = false;
34323 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34326 this.useShim = false;
34331 if(!cfg.existingProxy){
34333 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34335 this.proxy = Roo.get(cfg.existingProxy).dom;
34338 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34341 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34344 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34347 this.dragSpecs = {};
34350 * @private The adapter to use to positon and resize elements
34352 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34353 this.adapter.init(this);
34355 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34357 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34358 this.el.addClass("roo-splitbar-h");
34361 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34362 this.el.addClass("roo-splitbar-v");
34368 * Fires when the splitter is moved (alias for {@link #event-moved})
34369 * @param {Roo.bootstrap.SplitBar} this
34370 * @param {Number} newSize the new width or height
34375 * Fires when the splitter is moved
34376 * @param {Roo.bootstrap.SplitBar} this
34377 * @param {Number} newSize the new width or height
34381 * @event beforeresize
34382 * Fires before the splitter is dragged
34383 * @param {Roo.bootstrap.SplitBar} this
34385 "beforeresize" : true,
34387 "beforeapply" : true
34390 Roo.util.Observable.call(this);
34393 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34394 onStartProxyDrag : function(x, y){
34395 this.fireEvent("beforeresize", this);
34397 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34399 o.enableDisplayMode("block");
34400 // all splitbars share the same overlay
34401 Roo.bootstrap.SplitBar.prototype.overlay = o;
34403 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34404 this.overlay.show();
34405 Roo.get(this.proxy).setDisplayed("block");
34406 var size = this.adapter.getElementSize(this);
34407 this.activeMinSize = this.getMinimumSize();;
34408 this.activeMaxSize = this.getMaximumSize();;
34409 var c1 = size - this.activeMinSize;
34410 var c2 = Math.max(this.activeMaxSize - size, 0);
34411 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34412 this.dd.resetConstraints();
34413 this.dd.setXConstraint(
34414 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34415 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34417 this.dd.setYConstraint(0, 0);
34419 this.dd.resetConstraints();
34420 this.dd.setXConstraint(0, 0);
34421 this.dd.setYConstraint(
34422 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34423 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34426 this.dragSpecs.startSize = size;
34427 this.dragSpecs.startPoint = [x, y];
34428 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34432 * @private Called after the drag operation by the DDProxy
34434 onEndProxyDrag : function(e){
34435 Roo.get(this.proxy).setDisplayed(false);
34436 var endPoint = Roo.lib.Event.getXY(e);
34438 this.overlay.hide();
34441 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34442 newSize = this.dragSpecs.startSize +
34443 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34444 endPoint[0] - this.dragSpecs.startPoint[0] :
34445 this.dragSpecs.startPoint[0] - endPoint[0]
34448 newSize = this.dragSpecs.startSize +
34449 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34450 endPoint[1] - this.dragSpecs.startPoint[1] :
34451 this.dragSpecs.startPoint[1] - endPoint[1]
34454 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34455 if(newSize != this.dragSpecs.startSize){
34456 if(this.fireEvent('beforeapply', this, newSize) !== false){
34457 this.adapter.setElementSize(this, newSize);
34458 this.fireEvent("moved", this, newSize);
34459 this.fireEvent("resize", this, newSize);
34465 * Get the adapter this SplitBar uses
34466 * @return The adapter object
34468 getAdapter : function(){
34469 return this.adapter;
34473 * Set the adapter this SplitBar uses
34474 * @param {Object} adapter A SplitBar adapter object
34476 setAdapter : function(adapter){
34477 this.adapter = adapter;
34478 this.adapter.init(this);
34482 * Gets the minimum size for the resizing element
34483 * @return {Number} The minimum size
34485 getMinimumSize : function(){
34486 return this.minSize;
34490 * Sets the minimum size for the resizing element
34491 * @param {Number} minSize The minimum size
34493 setMinimumSize : function(minSize){
34494 this.minSize = minSize;
34498 * Gets the maximum size for the resizing element
34499 * @return {Number} The maximum size
34501 getMaximumSize : function(){
34502 return this.maxSize;
34506 * Sets the maximum size for the resizing element
34507 * @param {Number} maxSize The maximum size
34509 setMaximumSize : function(maxSize){
34510 this.maxSize = maxSize;
34514 * Sets the initialize size for the resizing element
34515 * @param {Number} size The initial size
34517 setCurrentSize : function(size){
34518 var oldAnimate = this.animate;
34519 this.animate = false;
34520 this.adapter.setElementSize(this, size);
34521 this.animate = oldAnimate;
34525 * Destroy this splitbar.
34526 * @param {Boolean} removeEl True to remove the element
34528 destroy : function(removeEl){
34530 this.shim.remove();
34533 this.proxy.parentNode.removeChild(this.proxy);
34541 * @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.
34543 Roo.bootstrap.SplitBar.createProxy = function(dir){
34544 var proxy = new Roo.Element(document.createElement("div"));
34545 proxy.unselectable();
34546 var cls = 'roo-splitbar-proxy';
34547 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34548 document.body.appendChild(proxy.dom);
34553 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34554 * Default Adapter. It assumes the splitter and resizing element are not positioned
34555 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34557 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34560 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34561 // do nothing for now
34562 init : function(s){
34566 * Called before drag operations to get the current size of the resizing element.
34567 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34569 getElementSize : function(s){
34570 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34571 return s.resizingEl.getWidth();
34573 return s.resizingEl.getHeight();
34578 * Called after drag operations to set the size of the resizing element.
34579 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34580 * @param {Number} newSize The new size to set
34581 * @param {Function} onComplete A function to be invoked when resizing is complete
34583 setElementSize : function(s, newSize, onComplete){
34584 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34586 s.resizingEl.setWidth(newSize);
34588 onComplete(s, newSize);
34591 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34596 s.resizingEl.setHeight(newSize);
34598 onComplete(s, newSize);
34601 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34608 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34609 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34610 * Adapter that moves the splitter element to align with the resized sizing element.
34611 * Used with an absolute positioned SplitBar.
34612 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34613 * document.body, make sure you assign an id to the body element.
34615 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34616 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34617 this.container = Roo.get(container);
34620 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34621 init : function(s){
34622 this.basic.init(s);
34625 getElementSize : function(s){
34626 return this.basic.getElementSize(s);
34629 setElementSize : function(s, newSize, onComplete){
34630 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34633 moveSplitter : function(s){
34634 var yes = Roo.bootstrap.SplitBar;
34635 switch(s.placement){
34637 s.el.setX(s.resizingEl.getRight());
34640 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34643 s.el.setY(s.resizingEl.getBottom());
34646 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34653 * Orientation constant - Create a vertical SplitBar
34657 Roo.bootstrap.SplitBar.VERTICAL = 1;
34660 * Orientation constant - Create a horizontal SplitBar
34664 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34667 * Placement constant - The resizing element is to the left of the splitter element
34671 Roo.bootstrap.SplitBar.LEFT = 1;
34674 * Placement constant - The resizing element is to the right of the splitter element
34678 Roo.bootstrap.SplitBar.RIGHT = 2;
34681 * Placement constant - The resizing element is positioned above the splitter element
34685 Roo.bootstrap.SplitBar.TOP = 3;
34688 * Placement constant - The resizing element is positioned under splitter element
34692 Roo.bootstrap.SplitBar.BOTTOM = 4;
34693 Roo.namespace("Roo.bootstrap.layout");/*
34695 * Ext JS Library 1.1.1
34696 * Copyright(c) 2006-2007, Ext JS, LLC.
34698 * Originally Released Under LGPL - original licence link has changed is not relivant.
34701 * <script type="text/javascript">
34705 * @class Roo.bootstrap.layout.Manager
34706 * @extends Roo.bootstrap.Component
34707 * Base class for layout managers.
34709 Roo.bootstrap.layout.Manager = function(config)
34711 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34717 /** false to disable window resize monitoring @type Boolean */
34718 this.monitorWindowResize = true;
34723 * Fires when a layout is performed.
34724 * @param {Roo.LayoutManager} this
34728 * @event regionresized
34729 * Fires when the user resizes a region.
34730 * @param {Roo.LayoutRegion} region The resized region
34731 * @param {Number} newSize The new size (width for east/west, height for north/south)
34733 "regionresized" : true,
34735 * @event regioncollapsed
34736 * Fires when a region is collapsed.
34737 * @param {Roo.LayoutRegion} region The collapsed region
34739 "regioncollapsed" : true,
34741 * @event regionexpanded
34742 * Fires when a region is expanded.
34743 * @param {Roo.LayoutRegion} region The expanded region
34745 "regionexpanded" : true
34747 this.updating = false;
34750 this.el = Roo.get(config.el);
34756 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34761 monitorWindowResize : true,
34767 onRender : function(ct, position)
34770 this.el = Roo.get(ct);
34773 //this.fireEvent('render',this);
34777 initEvents: function()
34781 // ie scrollbar fix
34782 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34783 document.body.scroll = "no";
34784 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34785 this.el.position('relative');
34787 this.id = this.el.id;
34788 this.el.addClass("roo-layout-container");
34789 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34790 if(this.el.dom != document.body ) {
34791 this.el.on('resize', this.layout,this);
34792 this.el.on('show', this.layout,this);
34798 * Returns true if this layout is currently being updated
34799 * @return {Boolean}
34801 isUpdating : function(){
34802 return this.updating;
34806 * Suspend the LayoutManager from doing auto-layouts while
34807 * making multiple add or remove calls
34809 beginUpdate : function(){
34810 this.updating = true;
34814 * Restore auto-layouts and optionally disable the manager from performing a layout
34815 * @param {Boolean} noLayout true to disable a layout update
34817 endUpdate : function(noLayout){
34818 this.updating = false;
34824 layout: function(){
34828 onRegionResized : function(region, newSize){
34829 this.fireEvent("regionresized", region, newSize);
34833 onRegionCollapsed : function(region){
34834 this.fireEvent("regioncollapsed", region);
34837 onRegionExpanded : function(region){
34838 this.fireEvent("regionexpanded", region);
34842 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34843 * performs box-model adjustments.
34844 * @return {Object} The size as an object {width: (the width), height: (the height)}
34846 getViewSize : function()
34849 if(this.el.dom != document.body){
34850 size = this.el.getSize();
34852 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34854 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34855 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34860 * Returns the Element this layout is bound to.
34861 * @return {Roo.Element}
34863 getEl : function(){
34868 * Returns the specified region.
34869 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34870 * @return {Roo.LayoutRegion}
34872 getRegion : function(target){
34873 return this.regions[target.toLowerCase()];
34876 onWindowResize : function(){
34877 if(this.monitorWindowResize){
34884 * Ext JS Library 1.1.1
34885 * Copyright(c) 2006-2007, Ext JS, LLC.
34887 * Originally Released Under LGPL - original licence link has changed is not relivant.
34890 * <script type="text/javascript">
34893 * @class Roo.bootstrap.layout.Border
34894 * @extends Roo.bootstrap.layout.Manager
34895 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34896 * please see: examples/bootstrap/nested.html<br><br>
34898 <b>The container the layout is rendered into can be either the body element or any other element.
34899 If it is not the body element, the container needs to either be an absolute positioned element,
34900 or you will need to add "position:relative" to the css of the container. You will also need to specify
34901 the container size if it is not the body element.</b>
34904 * Create a new Border
34905 * @param {Object} config Configuration options
34907 Roo.bootstrap.layout.Border = function(config){
34908 config = config || {};
34909 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34913 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34914 if(config[region]){
34915 config[region].region = region;
34916 this.addRegion(config[region]);
34922 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34924 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34926 * Creates and adds a new region if it doesn't already exist.
34927 * @param {String} target The target region key (north, south, east, west or center).
34928 * @param {Object} config The regions config object
34929 * @return {BorderLayoutRegion} The new region
34931 addRegion : function(config)
34933 if(!this.regions[config.region]){
34934 var r = this.factory(config);
34935 this.bindRegion(r);
34937 return this.regions[config.region];
34941 bindRegion : function(r){
34942 this.regions[r.config.region] = r;
34944 r.on("visibilitychange", this.layout, this);
34945 r.on("paneladded", this.layout, this);
34946 r.on("panelremoved", this.layout, this);
34947 r.on("invalidated", this.layout, this);
34948 r.on("resized", this.onRegionResized, this);
34949 r.on("collapsed", this.onRegionCollapsed, this);
34950 r.on("expanded", this.onRegionExpanded, this);
34954 * Performs a layout update.
34956 layout : function()
34958 if(this.updating) {
34962 // render all the rebions if they have not been done alreayd?
34963 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34964 if(this.regions[region] && !this.regions[region].bodyEl){
34965 this.regions[region].onRender(this.el)
34969 var size = this.getViewSize();
34970 var w = size.width;
34971 var h = size.height;
34976 //var x = 0, y = 0;
34978 var rs = this.regions;
34979 var north = rs["north"];
34980 var south = rs["south"];
34981 var west = rs["west"];
34982 var east = rs["east"];
34983 var center = rs["center"];
34984 //if(this.hideOnLayout){ // not supported anymore
34985 //c.el.setStyle("display", "none");
34987 if(north && north.isVisible()){
34988 var b = north.getBox();
34989 var m = north.getMargins();
34990 b.width = w - (m.left+m.right);
34993 centerY = b.height + b.y + m.bottom;
34994 centerH -= centerY;
34995 north.updateBox(this.safeBox(b));
34997 if(south && south.isVisible()){
34998 var b = south.getBox();
34999 var m = south.getMargins();
35000 b.width = w - (m.left+m.right);
35002 var totalHeight = (b.height + m.top + m.bottom);
35003 b.y = h - totalHeight + m.top;
35004 centerH -= totalHeight;
35005 south.updateBox(this.safeBox(b));
35007 if(west && west.isVisible()){
35008 var b = west.getBox();
35009 var m = west.getMargins();
35010 b.height = centerH - (m.top+m.bottom);
35012 b.y = centerY + m.top;
35013 var totalWidth = (b.width + m.left + m.right);
35014 centerX += totalWidth;
35015 centerW -= totalWidth;
35016 west.updateBox(this.safeBox(b));
35018 if(east && east.isVisible()){
35019 var b = east.getBox();
35020 var m = east.getMargins();
35021 b.height = centerH - (m.top+m.bottom);
35022 var totalWidth = (b.width + m.left + m.right);
35023 b.x = w - totalWidth + m.left;
35024 b.y = centerY + m.top;
35025 centerW -= totalWidth;
35026 east.updateBox(this.safeBox(b));
35029 var m = center.getMargins();
35031 x: centerX + m.left,
35032 y: centerY + m.top,
35033 width: centerW - (m.left+m.right),
35034 height: centerH - (m.top+m.bottom)
35036 //if(this.hideOnLayout){
35037 //center.el.setStyle("display", "block");
35039 center.updateBox(this.safeBox(centerBox));
35042 this.fireEvent("layout", this);
35046 safeBox : function(box){
35047 box.width = Math.max(0, box.width);
35048 box.height = Math.max(0, box.height);
35053 * Adds a ContentPanel (or subclass) to this layout.
35054 * @param {String} target The target region key (north, south, east, west or center).
35055 * @param {Roo.ContentPanel} panel The panel to add
35056 * @return {Roo.ContentPanel} The added panel
35058 add : function(target, panel){
35060 target = target.toLowerCase();
35061 return this.regions[target].add(panel);
35065 * Remove a ContentPanel (or subclass) to this layout.
35066 * @param {String} target The target region key (north, south, east, west or center).
35067 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35068 * @return {Roo.ContentPanel} The removed panel
35070 remove : function(target, panel){
35071 target = target.toLowerCase();
35072 return this.regions[target].remove(panel);
35076 * Searches all regions for a panel with the specified id
35077 * @param {String} panelId
35078 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35080 findPanel : function(panelId){
35081 var rs = this.regions;
35082 for(var target in rs){
35083 if(typeof rs[target] != "function"){
35084 var p = rs[target].getPanel(panelId);
35094 * Searches all regions for a panel with the specified id and activates (shows) it.
35095 * @param {String/ContentPanel} panelId The panels id or the panel itself
35096 * @return {Roo.ContentPanel} The shown panel or null
35098 showPanel : function(panelId) {
35099 var rs = this.regions;
35100 for(var target in rs){
35101 var r = rs[target];
35102 if(typeof r != "function"){
35103 if(r.hasPanel(panelId)){
35104 return r.showPanel(panelId);
35112 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35113 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35116 restoreState : function(provider){
35118 provider = Roo.state.Manager;
35120 var sm = new Roo.LayoutStateManager();
35121 sm.init(this, provider);
35127 * Adds a xtype elements to the layout.
35131 xtype : 'ContentPanel',
35138 xtype : 'NestedLayoutPanel',
35144 items : [ ... list of content panels or nested layout panels.. ]
35148 * @param {Object} cfg Xtype definition of item to add.
35150 addxtype : function(cfg)
35152 // basically accepts a pannel...
35153 // can accept a layout region..!?!?
35154 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35157 // theory? children can only be panels??
35159 //if (!cfg.xtype.match(/Panel$/)) {
35164 if (typeof(cfg.region) == 'undefined') {
35165 Roo.log("Failed to add Panel, region was not set");
35169 var region = cfg.region;
35175 xitems = cfg.items;
35182 case 'Content': // ContentPanel (el, cfg)
35183 case 'Scroll': // ContentPanel (el, cfg)
35185 cfg.autoCreate = true;
35186 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35188 // var el = this.el.createChild();
35189 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35192 this.add(region, ret);
35196 case 'TreePanel': // our new panel!
35197 cfg.el = this.el.createChild();
35198 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35199 this.add(region, ret);
35204 // create a new Layout (which is a Border Layout...
35206 var clayout = cfg.layout;
35207 clayout.el = this.el.createChild();
35208 clayout.items = clayout.items || [];
35212 // replace this exitems with the clayout ones..
35213 xitems = clayout.items;
35215 // force background off if it's in center...
35216 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35217 cfg.background = false;
35219 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35222 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35223 //console.log('adding nested layout panel ' + cfg.toSource());
35224 this.add(region, ret);
35225 nb = {}; /// find first...
35230 // needs grid and region
35232 //var el = this.getRegion(region).el.createChild();
35234 *var el = this.el.createChild();
35235 // create the grid first...
35236 cfg.grid.container = el;
35237 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35240 if (region == 'center' && this.active ) {
35241 cfg.background = false;
35244 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35246 this.add(region, ret);
35248 if (cfg.background) {
35249 // render grid on panel activation (if panel background)
35250 ret.on('activate', function(gp) {
35251 if (!gp.grid.rendered) {
35252 // gp.grid.render(el);
35256 // cfg.grid.render(el);
35262 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35263 // it was the old xcomponent building that caused this before.
35264 // espeically if border is the top element in the tree.
35274 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35276 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35277 this.add(region, ret);
35281 throw "Can not add '" + cfg.xtype + "' to Border";
35287 this.beginUpdate();
35291 Roo.each(xitems, function(i) {
35292 region = nb && i.region ? i.region : false;
35294 var add = ret.addxtype(i);
35297 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35298 if (!i.background) {
35299 abn[region] = nb[region] ;
35306 // make the last non-background panel active..
35307 //if (nb) { Roo.log(abn); }
35310 for(var r in abn) {
35311 region = this.getRegion(r);
35313 // tried using nb[r], but it does not work..
35315 region.showPanel(abn[r]);
35326 factory : function(cfg)
35329 var validRegions = Roo.bootstrap.layout.Border.regions;
35331 var target = cfg.region;
35334 var r = Roo.bootstrap.layout;
35338 return new r.North(cfg);
35340 return new r.South(cfg);
35342 return new r.East(cfg);
35344 return new r.West(cfg);
35346 return new r.Center(cfg);
35348 throw 'Layout region "'+target+'" not supported.';
35355 * Ext JS Library 1.1.1
35356 * Copyright(c) 2006-2007, Ext JS, LLC.
35358 * Originally Released Under LGPL - original licence link has changed is not relivant.
35361 * <script type="text/javascript">
35365 * @class Roo.bootstrap.layout.Basic
35366 * @extends Roo.util.Observable
35367 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35368 * and does not have a titlebar, tabs or any other features. All it does is size and position
35369 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35370 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35371 * @cfg {string} region the region that it inhabits..
35372 * @cfg {bool} skipConfig skip config?
35376 Roo.bootstrap.layout.Basic = function(config){
35378 this.mgr = config.mgr;
35380 this.position = config.region;
35382 var skipConfig = config.skipConfig;
35386 * @scope Roo.BasicLayoutRegion
35390 * @event beforeremove
35391 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35392 * @param {Roo.LayoutRegion} this
35393 * @param {Roo.ContentPanel} panel The panel
35394 * @param {Object} e The cancel event object
35396 "beforeremove" : true,
35398 * @event invalidated
35399 * Fires when the layout for this region is changed.
35400 * @param {Roo.LayoutRegion} this
35402 "invalidated" : true,
35404 * @event visibilitychange
35405 * Fires when this region is shown or hidden
35406 * @param {Roo.LayoutRegion} this
35407 * @param {Boolean} visibility true or false
35409 "visibilitychange" : true,
35411 * @event paneladded
35412 * Fires when a panel is added.
35413 * @param {Roo.LayoutRegion} this
35414 * @param {Roo.ContentPanel} panel The panel
35416 "paneladded" : true,
35418 * @event panelremoved
35419 * Fires when a panel is removed.
35420 * @param {Roo.LayoutRegion} this
35421 * @param {Roo.ContentPanel} panel The panel
35423 "panelremoved" : true,
35425 * @event beforecollapse
35426 * Fires when this region before collapse.
35427 * @param {Roo.LayoutRegion} this
35429 "beforecollapse" : true,
35432 * Fires when this region is collapsed.
35433 * @param {Roo.LayoutRegion} this
35435 "collapsed" : true,
35438 * Fires when this region is expanded.
35439 * @param {Roo.LayoutRegion} this
35444 * Fires when this region is slid into view.
35445 * @param {Roo.LayoutRegion} this
35447 "slideshow" : true,
35450 * Fires when this region slides out of view.
35451 * @param {Roo.LayoutRegion} this
35453 "slidehide" : true,
35455 * @event panelactivated
35456 * Fires when a panel is activated.
35457 * @param {Roo.LayoutRegion} this
35458 * @param {Roo.ContentPanel} panel The activated panel
35460 "panelactivated" : true,
35463 * Fires when the user resizes this region.
35464 * @param {Roo.LayoutRegion} this
35465 * @param {Number} newSize The new size (width for east/west, height for north/south)
35469 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35470 this.panels = new Roo.util.MixedCollection();
35471 this.panels.getKey = this.getPanelId.createDelegate(this);
35473 this.activePanel = null;
35474 // ensure listeners are added...
35476 if (config.listeners || config.events) {
35477 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35478 listeners : config.listeners || {},
35479 events : config.events || {}
35483 if(skipConfig !== true){
35484 this.applyConfig(config);
35488 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35490 getPanelId : function(p){
35494 applyConfig : function(config){
35495 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35496 this.config = config;
35501 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35502 * the width, for horizontal (north, south) the height.
35503 * @param {Number} newSize The new width or height
35505 resizeTo : function(newSize){
35506 var el = this.el ? this.el :
35507 (this.activePanel ? this.activePanel.getEl() : null);
35509 switch(this.position){
35512 el.setWidth(newSize);
35513 this.fireEvent("resized", this, newSize);
35517 el.setHeight(newSize);
35518 this.fireEvent("resized", this, newSize);
35524 getBox : function(){
35525 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35528 getMargins : function(){
35529 return this.margins;
35532 updateBox : function(box){
35534 var el = this.activePanel.getEl();
35535 el.dom.style.left = box.x + "px";
35536 el.dom.style.top = box.y + "px";
35537 this.activePanel.setSize(box.width, box.height);
35541 * Returns the container element for this region.
35542 * @return {Roo.Element}
35544 getEl : function(){
35545 return this.activePanel;
35549 * Returns true if this region is currently visible.
35550 * @return {Boolean}
35552 isVisible : function(){
35553 return this.activePanel ? true : false;
35556 setActivePanel : function(panel){
35557 panel = this.getPanel(panel);
35558 if(this.activePanel && this.activePanel != panel){
35559 this.activePanel.setActiveState(false);
35560 this.activePanel.getEl().setLeftTop(-10000,-10000);
35562 this.activePanel = panel;
35563 panel.setActiveState(true);
35565 panel.setSize(this.box.width, this.box.height);
35567 this.fireEvent("panelactivated", this, panel);
35568 this.fireEvent("invalidated");
35572 * Show the specified panel.
35573 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35574 * @return {Roo.ContentPanel} The shown panel or null
35576 showPanel : function(panel){
35577 panel = this.getPanel(panel);
35579 this.setActivePanel(panel);
35585 * Get the active panel for this region.
35586 * @return {Roo.ContentPanel} The active panel or null
35588 getActivePanel : function(){
35589 return this.activePanel;
35593 * Add the passed ContentPanel(s)
35594 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35595 * @return {Roo.ContentPanel} The panel added (if only one was added)
35597 add : function(panel){
35598 if(arguments.length > 1){
35599 for(var i = 0, len = arguments.length; i < len; i++) {
35600 this.add(arguments[i]);
35604 if(this.hasPanel(panel)){
35605 this.showPanel(panel);
35608 var el = panel.getEl();
35609 if(el.dom.parentNode != this.mgr.el.dom){
35610 this.mgr.el.dom.appendChild(el.dom);
35612 if(panel.setRegion){
35613 panel.setRegion(this);
35615 this.panels.add(panel);
35616 el.setStyle("position", "absolute");
35617 if(!panel.background){
35618 this.setActivePanel(panel);
35619 if(this.config.initialSize && this.panels.getCount()==1){
35620 this.resizeTo(this.config.initialSize);
35623 this.fireEvent("paneladded", this, panel);
35628 * Returns true if the panel is in this region.
35629 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35630 * @return {Boolean}
35632 hasPanel : function(panel){
35633 if(typeof panel == "object"){ // must be panel obj
35634 panel = panel.getId();
35636 return this.getPanel(panel) ? true : false;
35640 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35641 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35642 * @param {Boolean} preservePanel Overrides the config preservePanel option
35643 * @return {Roo.ContentPanel} The panel that was removed
35645 remove : function(panel, preservePanel){
35646 panel = this.getPanel(panel);
35651 this.fireEvent("beforeremove", this, panel, e);
35652 if(e.cancel === true){
35655 var panelId = panel.getId();
35656 this.panels.removeKey(panelId);
35661 * Returns the panel specified or null if it's not in this region.
35662 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35663 * @return {Roo.ContentPanel}
35665 getPanel : function(id){
35666 if(typeof id == "object"){ // must be panel obj
35669 return this.panels.get(id);
35673 * Returns this regions position (north/south/east/west/center).
35676 getPosition: function(){
35677 return this.position;
35681 * Ext JS Library 1.1.1
35682 * Copyright(c) 2006-2007, Ext JS, LLC.
35684 * Originally Released Under LGPL - original licence link has changed is not relivant.
35687 * <script type="text/javascript">
35691 * @class Roo.bootstrap.layout.Region
35692 * @extends Roo.bootstrap.layout.Basic
35693 * This class represents a region in a layout manager.
35695 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35696 * @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})
35697 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35698 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35699 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35700 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35701 * @cfg {String} title The title for the region (overrides panel titles)
35702 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35703 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35704 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35705 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35706 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35707 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35708 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35709 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35710 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35711 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35713 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35714 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35715 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35716 * @cfg {Number} width For East/West panels
35717 * @cfg {Number} height For North/South panels
35718 * @cfg {Boolean} split To show the splitter
35719 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35721 * @cfg {string} cls Extra CSS classes to add to region
35723 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35724 * @cfg {string} region the region that it inhabits..
35727 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35728 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35730 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35731 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35732 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35734 Roo.bootstrap.layout.Region = function(config)
35736 this.applyConfig(config);
35738 var mgr = config.mgr;
35739 var pos = config.region;
35740 config.skipConfig = true;
35741 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35744 this.onRender(mgr.el);
35747 this.visible = true;
35748 this.collapsed = false;
35749 this.unrendered_panels = [];
35752 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35754 position: '', // set by wrapper (eg. north/south etc..)
35755 unrendered_panels : null, // unrendered panels.
35756 createBody : function(){
35757 /** This region's body element
35758 * @type Roo.Element */
35759 this.bodyEl = this.el.createChild({
35761 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35765 onRender: function(ctr, pos)
35767 var dh = Roo.DomHelper;
35768 /** This region's container element
35769 * @type Roo.Element */
35770 this.el = dh.append(ctr.dom, {
35772 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35774 /** This region's title element
35775 * @type Roo.Element */
35777 this.titleEl = dh.append(this.el.dom,
35780 unselectable: "on",
35781 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35783 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35784 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35787 this.titleEl.enableDisplayMode();
35788 /** This region's title text element
35789 * @type HTMLElement */
35790 this.titleTextEl = this.titleEl.dom.firstChild;
35791 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35793 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35794 this.closeBtn.enableDisplayMode();
35795 this.closeBtn.on("click", this.closeClicked, this);
35796 this.closeBtn.hide();
35798 this.createBody(this.config);
35799 if(this.config.hideWhenEmpty){
35801 this.on("paneladded", this.validateVisibility, this);
35802 this.on("panelremoved", this.validateVisibility, this);
35804 if(this.autoScroll){
35805 this.bodyEl.setStyle("overflow", "auto");
35807 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35809 //if(c.titlebar !== false){
35810 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35811 this.titleEl.hide();
35813 this.titleEl.show();
35814 if(this.config.title){
35815 this.titleTextEl.innerHTML = this.config.title;
35819 if(this.config.collapsed){
35820 this.collapse(true);
35822 if(this.config.hidden){
35826 if (this.unrendered_panels && this.unrendered_panels.length) {
35827 for (var i =0;i< this.unrendered_panels.length; i++) {
35828 this.add(this.unrendered_panels[i]);
35830 this.unrendered_panels = null;
35836 applyConfig : function(c)
35839 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35840 var dh = Roo.DomHelper;
35841 if(c.titlebar !== false){
35842 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35843 this.collapseBtn.on("click", this.collapse, this);
35844 this.collapseBtn.enableDisplayMode();
35846 if(c.showPin === true || this.showPin){
35847 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35848 this.stickBtn.enableDisplayMode();
35849 this.stickBtn.on("click", this.expand, this);
35850 this.stickBtn.hide();
35855 /** This region's collapsed element
35856 * @type Roo.Element */
35859 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35860 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35863 if(c.floatable !== false){
35864 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35865 this.collapsedEl.on("click", this.collapseClick, this);
35868 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35869 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35870 id: "message", unselectable: "on", style:{"float":"left"}});
35871 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35873 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35874 this.expandBtn.on("click", this.expand, this);
35878 if(this.collapseBtn){
35879 this.collapseBtn.setVisible(c.collapsible == true);
35882 this.cmargins = c.cmargins || this.cmargins ||
35883 (this.position == "west" || this.position == "east" ?
35884 {top: 0, left: 2, right:2, bottom: 0} :
35885 {top: 2, left: 0, right:0, bottom: 2});
35887 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35890 this.bottomTabs = c.tabPosition != "top";
35892 this.autoScroll = c.autoScroll || false;
35897 this.duration = c.duration || .30;
35898 this.slideDuration = c.slideDuration || .45;
35903 * Returns true if this region is currently visible.
35904 * @return {Boolean}
35906 isVisible : function(){
35907 return this.visible;
35911 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35912 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35914 //setCollapsedTitle : function(title){
35915 // title = title || " ";
35916 // if(this.collapsedTitleTextEl){
35917 // this.collapsedTitleTextEl.innerHTML = title;
35921 getBox : function(){
35923 // if(!this.collapsed){
35924 b = this.el.getBox(false, true);
35926 // b = this.collapsedEl.getBox(false, true);
35931 getMargins : function(){
35932 return this.margins;
35933 //return this.collapsed ? this.cmargins : this.margins;
35936 highlight : function(){
35937 this.el.addClass("x-layout-panel-dragover");
35940 unhighlight : function(){
35941 this.el.removeClass("x-layout-panel-dragover");
35944 updateBox : function(box)
35946 if (!this.bodyEl) {
35947 return; // not rendered yet..
35951 if(!this.collapsed){
35952 this.el.dom.style.left = box.x + "px";
35953 this.el.dom.style.top = box.y + "px";
35954 this.updateBody(box.width, box.height);
35956 this.collapsedEl.dom.style.left = box.x + "px";
35957 this.collapsedEl.dom.style.top = box.y + "px";
35958 this.collapsedEl.setSize(box.width, box.height);
35961 this.tabs.autoSizeTabs();
35965 updateBody : function(w, h)
35968 this.el.setWidth(w);
35969 w -= this.el.getBorderWidth("rl");
35970 if(this.config.adjustments){
35971 w += this.config.adjustments[0];
35974 if(h !== null && h > 0){
35975 this.el.setHeight(h);
35976 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35977 h -= this.el.getBorderWidth("tb");
35978 if(this.config.adjustments){
35979 h += this.config.adjustments[1];
35981 this.bodyEl.setHeight(h);
35983 h = this.tabs.syncHeight(h);
35986 if(this.panelSize){
35987 w = w !== null ? w : this.panelSize.width;
35988 h = h !== null ? h : this.panelSize.height;
35990 if(this.activePanel){
35991 var el = this.activePanel.getEl();
35992 w = w !== null ? w : el.getWidth();
35993 h = h !== null ? h : el.getHeight();
35994 this.panelSize = {width: w, height: h};
35995 this.activePanel.setSize(w, h);
35997 if(Roo.isIE && this.tabs){
35998 this.tabs.el.repaint();
36003 * Returns the container element for this region.
36004 * @return {Roo.Element}
36006 getEl : function(){
36011 * Hides this region.
36014 //if(!this.collapsed){
36015 this.el.dom.style.left = "-2000px";
36018 // this.collapsedEl.dom.style.left = "-2000px";
36019 // this.collapsedEl.hide();
36021 this.visible = false;
36022 this.fireEvent("visibilitychange", this, false);
36026 * Shows this region if it was previously hidden.
36029 //if(!this.collapsed){
36032 // this.collapsedEl.show();
36034 this.visible = true;
36035 this.fireEvent("visibilitychange", this, true);
36038 closeClicked : function(){
36039 if(this.activePanel){
36040 this.remove(this.activePanel);
36044 collapseClick : function(e){
36046 e.stopPropagation();
36049 e.stopPropagation();
36055 * Collapses this region.
36056 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36059 collapse : function(skipAnim, skipCheck = false){
36060 if(this.collapsed) {
36064 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36066 this.collapsed = true;
36068 this.split.el.hide();
36070 if(this.config.animate && skipAnim !== true){
36071 this.fireEvent("invalidated", this);
36072 this.animateCollapse();
36074 this.el.setLocation(-20000,-20000);
36076 this.collapsedEl.show();
36077 this.fireEvent("collapsed", this);
36078 this.fireEvent("invalidated", this);
36084 animateCollapse : function(){
36089 * Expands this region if it was previously collapsed.
36090 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36091 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36094 expand : function(e, skipAnim){
36096 e.stopPropagation();
36098 if(!this.collapsed || this.el.hasActiveFx()) {
36102 this.afterSlideIn();
36105 this.collapsed = false;
36106 if(this.config.animate && skipAnim !== true){
36107 this.animateExpand();
36111 this.split.el.show();
36113 this.collapsedEl.setLocation(-2000,-2000);
36114 this.collapsedEl.hide();
36115 this.fireEvent("invalidated", this);
36116 this.fireEvent("expanded", this);
36120 animateExpand : function(){
36124 initTabs : function()
36126 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36128 var ts = new Roo.bootstrap.panel.Tabs({
36129 el: this.bodyEl.dom,
36130 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36131 disableTooltips: this.config.disableTabTips,
36132 toolbar : this.config.toolbar
36135 if(this.config.hideTabs){
36136 ts.stripWrap.setDisplayed(false);
36139 ts.resizeTabs = this.config.resizeTabs === true;
36140 ts.minTabWidth = this.config.minTabWidth || 40;
36141 ts.maxTabWidth = this.config.maxTabWidth || 250;
36142 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36143 ts.monitorResize = false;
36144 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36145 ts.bodyEl.addClass('roo-layout-tabs-body');
36146 this.panels.each(this.initPanelAsTab, this);
36149 initPanelAsTab : function(panel){
36150 var ti = this.tabs.addTab(
36154 this.config.closeOnTab && panel.isClosable(),
36157 if(panel.tabTip !== undefined){
36158 ti.setTooltip(panel.tabTip);
36160 ti.on("activate", function(){
36161 this.setActivePanel(panel);
36164 if(this.config.closeOnTab){
36165 ti.on("beforeclose", function(t, e){
36167 this.remove(panel);
36171 panel.tabItem = ti;
36176 updatePanelTitle : function(panel, title)
36178 if(this.activePanel == panel){
36179 this.updateTitle(title);
36182 var ti = this.tabs.getTab(panel.getEl().id);
36184 if(panel.tabTip !== undefined){
36185 ti.setTooltip(panel.tabTip);
36190 updateTitle : function(title){
36191 if(this.titleTextEl && !this.config.title){
36192 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36196 setActivePanel : function(panel)
36198 panel = this.getPanel(panel);
36199 if(this.activePanel && this.activePanel != panel){
36200 if(this.activePanel.setActiveState(false) === false){
36204 this.activePanel = panel;
36205 panel.setActiveState(true);
36206 if(this.panelSize){
36207 panel.setSize(this.panelSize.width, this.panelSize.height);
36210 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36212 this.updateTitle(panel.getTitle());
36214 this.fireEvent("invalidated", this);
36216 this.fireEvent("panelactivated", this, panel);
36220 * Shows the specified panel.
36221 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36222 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36224 showPanel : function(panel)
36226 panel = this.getPanel(panel);
36229 var tab = this.tabs.getTab(panel.getEl().id);
36230 if(tab.isHidden()){
36231 this.tabs.unhideTab(tab.id);
36235 this.setActivePanel(panel);
36242 * Get the active panel for this region.
36243 * @return {Roo.ContentPanel} The active panel or null
36245 getActivePanel : function(){
36246 return this.activePanel;
36249 validateVisibility : function(){
36250 if(this.panels.getCount() < 1){
36251 this.updateTitle(" ");
36252 this.closeBtn.hide();
36255 if(!this.isVisible()){
36262 * Adds the passed ContentPanel(s) to this region.
36263 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36264 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36266 add : function(panel)
36268 if(arguments.length > 1){
36269 for(var i = 0, len = arguments.length; i < len; i++) {
36270 this.add(arguments[i]);
36275 // if we have not been rendered yet, then we can not really do much of this..
36276 if (!this.bodyEl) {
36277 this.unrendered_panels.push(panel);
36284 if(this.hasPanel(panel)){
36285 this.showPanel(panel);
36288 panel.setRegion(this);
36289 this.panels.add(panel);
36290 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36291 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36292 // and hide them... ???
36293 this.bodyEl.dom.appendChild(panel.getEl().dom);
36294 if(panel.background !== true){
36295 this.setActivePanel(panel);
36297 this.fireEvent("paneladded", this, panel);
36304 this.initPanelAsTab(panel);
36308 if(panel.background !== true){
36309 this.tabs.activate(panel.getEl().id);
36311 this.fireEvent("paneladded", this, panel);
36316 * Hides the tab for the specified panel.
36317 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36319 hidePanel : function(panel){
36320 if(this.tabs && (panel = this.getPanel(panel))){
36321 this.tabs.hideTab(panel.getEl().id);
36326 * Unhides the tab for a previously hidden panel.
36327 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36329 unhidePanel : function(panel){
36330 if(this.tabs && (panel = this.getPanel(panel))){
36331 this.tabs.unhideTab(panel.getEl().id);
36335 clearPanels : function(){
36336 while(this.panels.getCount() > 0){
36337 this.remove(this.panels.first());
36342 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36343 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36344 * @param {Boolean} preservePanel Overrides the config preservePanel option
36345 * @return {Roo.ContentPanel} The panel that was removed
36347 remove : function(panel, preservePanel)
36349 panel = this.getPanel(panel);
36354 this.fireEvent("beforeremove", this, panel, e);
36355 if(e.cancel === true){
36358 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36359 var panelId = panel.getId();
36360 this.panels.removeKey(panelId);
36362 document.body.appendChild(panel.getEl().dom);
36365 this.tabs.removeTab(panel.getEl().id);
36366 }else if (!preservePanel){
36367 this.bodyEl.dom.removeChild(panel.getEl().dom);
36369 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36370 var p = this.panels.first();
36371 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36372 tempEl.appendChild(p.getEl().dom);
36373 this.bodyEl.update("");
36374 this.bodyEl.dom.appendChild(p.getEl().dom);
36376 this.updateTitle(p.getTitle());
36378 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36379 this.setActivePanel(p);
36381 panel.setRegion(null);
36382 if(this.activePanel == panel){
36383 this.activePanel = null;
36385 if(this.config.autoDestroy !== false && preservePanel !== true){
36386 try{panel.destroy();}catch(e){}
36388 this.fireEvent("panelremoved", this, panel);
36393 * Returns the TabPanel component used by this region
36394 * @return {Roo.TabPanel}
36396 getTabs : function(){
36400 createTool : function(parentEl, className){
36401 var btn = Roo.DomHelper.append(parentEl, {
36403 cls: "x-layout-tools-button",
36406 cls: "roo-layout-tools-button-inner " + className,
36410 btn.addClassOnOver("roo-layout-tools-button-over");
36415 * Ext JS Library 1.1.1
36416 * Copyright(c) 2006-2007, Ext JS, LLC.
36418 * Originally Released Under LGPL - original licence link has changed is not relivant.
36421 * <script type="text/javascript">
36427 * @class Roo.SplitLayoutRegion
36428 * @extends Roo.LayoutRegion
36429 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36431 Roo.bootstrap.layout.Split = function(config){
36432 this.cursor = config.cursor;
36433 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36436 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36438 splitTip : "Drag to resize.",
36439 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36440 useSplitTips : false,
36442 applyConfig : function(config){
36443 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36446 onRender : function(ctr,pos) {
36448 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36449 if(!this.config.split){
36454 var splitEl = Roo.DomHelper.append(ctr.dom, {
36456 id: this.el.id + "-split",
36457 cls: "roo-layout-split roo-layout-split-"+this.position,
36460 /** The SplitBar for this region
36461 * @type Roo.SplitBar */
36462 // does not exist yet...
36463 Roo.log([this.position, this.orientation]);
36465 this.split = new Roo.bootstrap.SplitBar({
36466 dragElement : splitEl,
36467 resizingElement: this.el,
36468 orientation : this.orientation
36471 this.split.on("moved", this.onSplitMove, this);
36472 this.split.useShim = this.config.useShim === true;
36473 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36474 if(this.useSplitTips){
36475 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36477 //if(config.collapsible){
36478 // this.split.el.on("dblclick", this.collapse, this);
36481 if(typeof this.config.minSize != "undefined"){
36482 this.split.minSize = this.config.minSize;
36484 if(typeof this.config.maxSize != "undefined"){
36485 this.split.maxSize = this.config.maxSize;
36487 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36488 this.hideSplitter();
36493 getHMaxSize : function(){
36494 var cmax = this.config.maxSize || 10000;
36495 var center = this.mgr.getRegion("center");
36496 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36499 getVMaxSize : function(){
36500 var cmax = this.config.maxSize || 10000;
36501 var center = this.mgr.getRegion("center");
36502 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36505 onSplitMove : function(split, newSize){
36506 this.fireEvent("resized", this, newSize);
36510 * Returns the {@link Roo.SplitBar} for this region.
36511 * @return {Roo.SplitBar}
36513 getSplitBar : function(){
36518 this.hideSplitter();
36519 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36522 hideSplitter : function(){
36524 this.split.el.setLocation(-2000,-2000);
36525 this.split.el.hide();
36531 this.split.el.show();
36533 Roo.bootstrap.layout.Split.superclass.show.call(this);
36536 beforeSlide: function(){
36537 if(Roo.isGecko){// firefox overflow auto bug workaround
36538 this.bodyEl.clip();
36540 this.tabs.bodyEl.clip();
36542 if(this.activePanel){
36543 this.activePanel.getEl().clip();
36545 if(this.activePanel.beforeSlide){
36546 this.activePanel.beforeSlide();
36552 afterSlide : function(){
36553 if(Roo.isGecko){// firefox overflow auto bug workaround
36554 this.bodyEl.unclip();
36556 this.tabs.bodyEl.unclip();
36558 if(this.activePanel){
36559 this.activePanel.getEl().unclip();
36560 if(this.activePanel.afterSlide){
36561 this.activePanel.afterSlide();
36567 initAutoHide : function(){
36568 if(this.autoHide !== false){
36569 if(!this.autoHideHd){
36570 var st = new Roo.util.DelayedTask(this.slideIn, this);
36571 this.autoHideHd = {
36572 "mouseout": function(e){
36573 if(!e.within(this.el, true)){
36577 "mouseover" : function(e){
36583 this.el.on(this.autoHideHd);
36587 clearAutoHide : function(){
36588 if(this.autoHide !== false){
36589 this.el.un("mouseout", this.autoHideHd.mouseout);
36590 this.el.un("mouseover", this.autoHideHd.mouseover);
36594 clearMonitor : function(){
36595 Roo.get(document).un("click", this.slideInIf, this);
36598 // these names are backwards but not changed for compat
36599 slideOut : function(){
36600 if(this.isSlid || this.el.hasActiveFx()){
36603 this.isSlid = true;
36604 if(this.collapseBtn){
36605 this.collapseBtn.hide();
36607 this.closeBtnState = this.closeBtn.getStyle('display');
36608 this.closeBtn.hide();
36610 this.stickBtn.show();
36613 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36614 this.beforeSlide();
36615 this.el.setStyle("z-index", 10001);
36616 this.el.slideIn(this.getSlideAnchor(), {
36617 callback: function(){
36619 this.initAutoHide();
36620 Roo.get(document).on("click", this.slideInIf, this);
36621 this.fireEvent("slideshow", this);
36628 afterSlideIn : function(){
36629 this.clearAutoHide();
36630 this.isSlid = false;
36631 this.clearMonitor();
36632 this.el.setStyle("z-index", "");
36633 if(this.collapseBtn){
36634 this.collapseBtn.show();
36636 this.closeBtn.setStyle('display', this.closeBtnState);
36638 this.stickBtn.hide();
36640 this.fireEvent("slidehide", this);
36643 slideIn : function(cb){
36644 if(!this.isSlid || this.el.hasActiveFx()){
36648 this.isSlid = false;
36649 this.beforeSlide();
36650 this.el.slideOut(this.getSlideAnchor(), {
36651 callback: function(){
36652 this.el.setLeftTop(-10000, -10000);
36654 this.afterSlideIn();
36662 slideInIf : function(e){
36663 if(!e.within(this.el)){
36668 animateCollapse : function(){
36669 this.beforeSlide();
36670 this.el.setStyle("z-index", 20000);
36671 var anchor = this.getSlideAnchor();
36672 this.el.slideOut(anchor, {
36673 callback : function(){
36674 this.el.setStyle("z-index", "");
36675 this.collapsedEl.slideIn(anchor, {duration:.3});
36677 this.el.setLocation(-10000,-10000);
36679 this.fireEvent("collapsed", this);
36686 animateExpand : function(){
36687 this.beforeSlide();
36688 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36689 this.el.setStyle("z-index", 20000);
36690 this.collapsedEl.hide({
36693 this.el.slideIn(this.getSlideAnchor(), {
36694 callback : function(){
36695 this.el.setStyle("z-index", "");
36698 this.split.el.show();
36700 this.fireEvent("invalidated", this);
36701 this.fireEvent("expanded", this);
36729 getAnchor : function(){
36730 return this.anchors[this.position];
36733 getCollapseAnchor : function(){
36734 return this.canchors[this.position];
36737 getSlideAnchor : function(){
36738 return this.sanchors[this.position];
36741 getAlignAdj : function(){
36742 var cm = this.cmargins;
36743 switch(this.position){
36759 getExpandAdj : function(){
36760 var c = this.collapsedEl, cm = this.cmargins;
36761 switch(this.position){
36763 return [-(cm.right+c.getWidth()+cm.left), 0];
36766 return [cm.right+c.getWidth()+cm.left, 0];
36769 return [0, -(cm.top+cm.bottom+c.getHeight())];
36772 return [0, cm.top+cm.bottom+c.getHeight()];
36778 * Ext JS Library 1.1.1
36779 * Copyright(c) 2006-2007, Ext JS, LLC.
36781 * Originally Released Under LGPL - original licence link has changed is not relivant.
36784 * <script type="text/javascript">
36787 * These classes are private internal classes
36789 Roo.bootstrap.layout.Center = function(config){
36790 config.region = "center";
36791 Roo.bootstrap.layout.Region.call(this, config);
36792 this.visible = true;
36793 this.minWidth = config.minWidth || 20;
36794 this.minHeight = config.minHeight || 20;
36797 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36799 // center panel can't be hidden
36803 // center panel can't be hidden
36806 getMinWidth: function(){
36807 return this.minWidth;
36810 getMinHeight: function(){
36811 return this.minHeight;
36824 Roo.bootstrap.layout.North = function(config)
36826 config.region = 'north';
36827 config.cursor = 'n-resize';
36829 Roo.bootstrap.layout.Split.call(this, config);
36833 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36834 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36835 this.split.el.addClass("roo-layout-split-v");
36837 var size = config.initialSize || config.height;
36838 if(typeof size != "undefined"){
36839 this.el.setHeight(size);
36842 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36844 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36848 getBox : function(){
36849 if(this.collapsed){
36850 return this.collapsedEl.getBox();
36852 var box = this.el.getBox();
36854 box.height += this.split.el.getHeight();
36859 updateBox : function(box){
36860 if(this.split && !this.collapsed){
36861 box.height -= this.split.el.getHeight();
36862 this.split.el.setLeft(box.x);
36863 this.split.el.setTop(box.y+box.height);
36864 this.split.el.setWidth(box.width);
36866 if(this.collapsed){
36867 this.updateBody(box.width, null);
36869 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36877 Roo.bootstrap.layout.South = function(config){
36878 config.region = 'south';
36879 config.cursor = 's-resize';
36880 Roo.bootstrap.layout.Split.call(this, config);
36882 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36883 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36884 this.split.el.addClass("roo-layout-split-v");
36886 var size = config.initialSize || config.height;
36887 if(typeof size != "undefined"){
36888 this.el.setHeight(size);
36892 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36893 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36894 getBox : function(){
36895 if(this.collapsed){
36896 return this.collapsedEl.getBox();
36898 var box = this.el.getBox();
36900 var sh = this.split.el.getHeight();
36907 updateBox : function(box){
36908 if(this.split && !this.collapsed){
36909 var sh = this.split.el.getHeight();
36912 this.split.el.setLeft(box.x);
36913 this.split.el.setTop(box.y-sh);
36914 this.split.el.setWidth(box.width);
36916 if(this.collapsed){
36917 this.updateBody(box.width, null);
36919 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36923 Roo.bootstrap.layout.East = function(config){
36924 config.region = "east";
36925 config.cursor = "e-resize";
36926 Roo.bootstrap.layout.Split.call(this, config);
36928 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36929 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36930 this.split.el.addClass("roo-layout-split-h");
36932 var size = config.initialSize || config.width;
36933 if(typeof size != "undefined"){
36934 this.el.setWidth(size);
36937 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36938 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36939 getBox : function(){
36940 if(this.collapsed){
36941 return this.collapsedEl.getBox();
36943 var box = this.el.getBox();
36945 var sw = this.split.el.getWidth();
36952 updateBox : function(box){
36953 if(this.split && !this.collapsed){
36954 var sw = this.split.el.getWidth();
36956 this.split.el.setLeft(box.x);
36957 this.split.el.setTop(box.y);
36958 this.split.el.setHeight(box.height);
36961 if(this.collapsed){
36962 this.updateBody(null, box.height);
36964 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36968 Roo.bootstrap.layout.West = function(config){
36969 config.region = "west";
36970 config.cursor = "w-resize";
36972 Roo.bootstrap.layout.Split.call(this, config);
36974 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36975 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36976 this.split.el.addClass("roo-layout-split-h");
36980 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36981 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36983 onRender: function(ctr, pos)
36985 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36986 var size = this.config.initialSize || this.config.width;
36987 if(typeof size != "undefined"){
36988 this.el.setWidth(size);
36992 getBox : function(){
36993 if(this.collapsed){
36994 return this.collapsedEl.getBox();
36996 var box = this.el.getBox();
36998 box.width += this.split.el.getWidth();
37003 updateBox : function(box){
37004 if(this.split && !this.collapsed){
37005 var sw = this.split.el.getWidth();
37007 this.split.el.setLeft(box.x+box.width);
37008 this.split.el.setTop(box.y);
37009 this.split.el.setHeight(box.height);
37011 if(this.collapsed){
37012 this.updateBody(null, box.height);
37014 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37017 Roo.namespace("Roo.bootstrap.panel");/*
37019 * Ext JS Library 1.1.1
37020 * Copyright(c) 2006-2007, Ext JS, LLC.
37022 * Originally Released Under LGPL - original licence link has changed is not relivant.
37025 * <script type="text/javascript">
37028 * @class Roo.ContentPanel
37029 * @extends Roo.util.Observable
37030 * A basic ContentPanel element.
37031 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37032 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37033 * @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
37034 * @cfg {Boolean} closable True if the panel can be closed/removed
37035 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37036 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37037 * @cfg {Toolbar} toolbar A toolbar for this panel
37038 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37039 * @cfg {String} title The title for this panel
37040 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37041 * @cfg {String} url Calls {@link #setUrl} with this value
37042 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37043 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37044 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37045 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37046 * @cfg {Boolean} badges render the badges
37049 * Create a new ContentPanel.
37050 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37051 * @param {String/Object} config A string to set only the title or a config object
37052 * @param {String} content (optional) Set the HTML content for this panel
37053 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37055 Roo.bootstrap.panel.Content = function( config){
37057 this.tpl = config.tpl || false;
37059 var el = config.el;
37060 var content = config.content;
37062 if(config.autoCreate){ // xtype is available if this is called from factory
37065 this.el = Roo.get(el);
37066 if(!this.el && config && config.autoCreate){
37067 if(typeof config.autoCreate == "object"){
37068 if(!config.autoCreate.id){
37069 config.autoCreate.id = config.id||el;
37071 this.el = Roo.DomHelper.append(document.body,
37072 config.autoCreate, true);
37074 var elcfg = { tag: "div",
37075 cls: "roo-layout-inactive-content",
37079 elcfg.html = config.html;
37083 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37086 this.closable = false;
37087 this.loaded = false;
37088 this.active = false;
37091 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37093 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37095 this.wrapEl = this.el; //this.el.wrap();
37097 if (config.toolbar.items) {
37098 ti = config.toolbar.items ;
37099 delete config.toolbar.items ;
37103 this.toolbar.render(this.wrapEl, 'before');
37104 for(var i =0;i < ti.length;i++) {
37105 // Roo.log(['add child', items[i]]);
37106 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37108 this.toolbar.items = nitems;
37109 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37110 delete config.toolbar;
37114 // xtype created footer. - not sure if will work as we normally have to render first..
37115 if (this.footer && !this.footer.el && this.footer.xtype) {
37116 if (!this.wrapEl) {
37117 this.wrapEl = this.el.wrap();
37120 this.footer.container = this.wrapEl.createChild();
37122 this.footer = Roo.factory(this.footer, Roo);
37127 if(typeof config == "string"){
37128 this.title = config;
37130 Roo.apply(this, config);
37134 this.resizeEl = Roo.get(this.resizeEl, true);
37136 this.resizeEl = this.el;
37138 // handle view.xtype
37146 * Fires when this panel is activated.
37147 * @param {Roo.ContentPanel} this
37151 * @event deactivate
37152 * Fires when this panel is activated.
37153 * @param {Roo.ContentPanel} this
37155 "deactivate" : true,
37159 * Fires when this panel is resized if fitToFrame is true.
37160 * @param {Roo.ContentPanel} this
37161 * @param {Number} width The width after any component adjustments
37162 * @param {Number} height The height after any component adjustments
37168 * Fires when this tab is created
37169 * @param {Roo.ContentPanel} this
37180 if(this.autoScroll){
37181 this.resizeEl.setStyle("overflow", "auto");
37183 // fix randome scrolling
37184 //this.el.on('scroll', function() {
37185 // Roo.log('fix random scolling');
37186 // this.scrollTo('top',0);
37189 content = content || this.content;
37191 this.setContent(content);
37193 if(config && config.url){
37194 this.setUrl(this.url, this.params, this.loadOnce);
37199 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37201 if (this.view && typeof(this.view.xtype) != 'undefined') {
37202 this.view.el = this.el.appendChild(document.createElement("div"));
37203 this.view = Roo.factory(this.view);
37204 this.view.render && this.view.render(false, '');
37208 this.fireEvent('render', this);
37211 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37215 setRegion : function(region){
37216 this.region = region;
37217 this.setActiveClass(region && !this.background);
37221 setActiveClass: function(state)
37224 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37225 this.el.setStyle('position','relative');
37227 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37228 this.el.setStyle('position', 'absolute');
37233 * Returns the toolbar for this Panel if one was configured.
37234 * @return {Roo.Toolbar}
37236 getToolbar : function(){
37237 return this.toolbar;
37240 setActiveState : function(active)
37242 this.active = active;
37243 this.setActiveClass(active);
37245 if(this.fireEvent("deactivate", this) === false){
37250 this.fireEvent("activate", this);
37254 * Updates this panel's element
37255 * @param {String} content The new content
37256 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37258 setContent : function(content, loadScripts){
37259 this.el.update(content, loadScripts);
37262 ignoreResize : function(w, h){
37263 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37266 this.lastSize = {width: w, height: h};
37271 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37272 * @return {Roo.UpdateManager} The UpdateManager
37274 getUpdateManager : function(){
37275 return this.el.getUpdateManager();
37278 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37279 * @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:
37282 url: "your-url.php",
37283 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37284 callback: yourFunction,
37285 scope: yourObject, //(optional scope)
37288 text: "Loading...",
37293 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37294 * 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.
37295 * @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}
37296 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37297 * @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.
37298 * @return {Roo.ContentPanel} this
37301 var um = this.el.getUpdateManager();
37302 um.update.apply(um, arguments);
37308 * 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.
37309 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37310 * @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)
37311 * @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)
37312 * @return {Roo.UpdateManager} The UpdateManager
37314 setUrl : function(url, params, loadOnce){
37315 if(this.refreshDelegate){
37316 this.removeListener("activate", this.refreshDelegate);
37318 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37319 this.on("activate", this.refreshDelegate);
37320 return this.el.getUpdateManager();
37323 _handleRefresh : function(url, params, loadOnce){
37324 if(!loadOnce || !this.loaded){
37325 var updater = this.el.getUpdateManager();
37326 updater.update(url, params, this._setLoaded.createDelegate(this));
37330 _setLoaded : function(){
37331 this.loaded = true;
37335 * Returns this panel's id
37338 getId : function(){
37343 * Returns this panel's element - used by regiosn to add.
37344 * @return {Roo.Element}
37346 getEl : function(){
37347 return this.wrapEl || this.el;
37352 adjustForComponents : function(width, height)
37354 //Roo.log('adjustForComponents ');
37355 if(this.resizeEl != this.el){
37356 width -= this.el.getFrameWidth('lr');
37357 height -= this.el.getFrameWidth('tb');
37360 var te = this.toolbar.getEl();
37361 te.setWidth(width);
37362 height -= te.getHeight();
37365 var te = this.footer.getEl();
37366 te.setWidth(width);
37367 height -= te.getHeight();
37371 if(this.adjustments){
37372 width += this.adjustments[0];
37373 height += this.adjustments[1];
37375 return {"width": width, "height": height};
37378 setSize : function(width, height){
37379 if(this.fitToFrame && !this.ignoreResize(width, height)){
37380 if(this.fitContainer && this.resizeEl != this.el){
37381 this.el.setSize(width, height);
37383 var size = this.adjustForComponents(width, height);
37384 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37385 this.fireEvent('resize', this, size.width, size.height);
37390 * Returns this panel's title
37393 getTitle : function(){
37395 if (typeof(this.title) != 'object') {
37400 for (var k in this.title) {
37401 if (!this.title.hasOwnProperty(k)) {
37405 if (k.indexOf('-') >= 0) {
37406 var s = k.split('-');
37407 for (var i = 0; i<s.length; i++) {
37408 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37411 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37418 * Set this panel's title
37419 * @param {String} title
37421 setTitle : function(title){
37422 this.title = title;
37424 this.region.updatePanelTitle(this, title);
37429 * Returns true is this panel was configured to be closable
37430 * @return {Boolean}
37432 isClosable : function(){
37433 return this.closable;
37436 beforeSlide : function(){
37438 this.resizeEl.clip();
37441 afterSlide : function(){
37443 this.resizeEl.unclip();
37447 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37448 * Will fail silently if the {@link #setUrl} method has not been called.
37449 * This does not activate the panel, just updates its content.
37451 refresh : function(){
37452 if(this.refreshDelegate){
37453 this.loaded = false;
37454 this.refreshDelegate();
37459 * Destroys this panel
37461 destroy : function(){
37462 this.el.removeAllListeners();
37463 var tempEl = document.createElement("span");
37464 tempEl.appendChild(this.el.dom);
37465 tempEl.innerHTML = "";
37471 * form - if the content panel contains a form - this is a reference to it.
37472 * @type {Roo.form.Form}
37476 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37477 * This contains a reference to it.
37483 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37493 * @param {Object} cfg Xtype definition of item to add.
37497 getChildContainer: function () {
37498 return this.getEl();
37503 var ret = new Roo.factory(cfg);
37508 if (cfg.xtype.match(/^Form$/)) {
37511 //if (this.footer) {
37512 // el = this.footer.container.insertSibling(false, 'before');
37514 el = this.el.createChild();
37517 this.form = new Roo.form.Form(cfg);
37520 if ( this.form.allItems.length) {
37521 this.form.render(el.dom);
37525 // should only have one of theses..
37526 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37527 // views.. should not be just added - used named prop 'view''
37529 cfg.el = this.el.appendChild(document.createElement("div"));
37532 var ret = new Roo.factory(cfg);
37534 ret.render && ret.render(false, ''); // render blank..
37544 * @class Roo.bootstrap.panel.Grid
37545 * @extends Roo.bootstrap.panel.Content
37547 * Create a new GridPanel.
37548 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37549 * @param {Object} config A the config object
37555 Roo.bootstrap.panel.Grid = function(config)
37559 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37560 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37562 config.el = this.wrapper;
37563 //this.el = this.wrapper;
37565 if (config.container) {
37566 // ctor'ed from a Border/panel.grid
37569 this.wrapper.setStyle("overflow", "hidden");
37570 this.wrapper.addClass('roo-grid-container');
37575 if(config.toolbar){
37576 var tool_el = this.wrapper.createChild();
37577 this.toolbar = Roo.factory(config.toolbar);
37579 if (config.toolbar.items) {
37580 ti = config.toolbar.items ;
37581 delete config.toolbar.items ;
37585 this.toolbar.render(tool_el);
37586 for(var i =0;i < ti.length;i++) {
37587 // Roo.log(['add child', items[i]]);
37588 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37590 this.toolbar.items = nitems;
37592 delete config.toolbar;
37595 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37596 config.grid.scrollBody = true;;
37597 config.grid.monitorWindowResize = false; // turn off autosizing
37598 config.grid.autoHeight = false;
37599 config.grid.autoWidth = false;
37601 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37603 if (config.background) {
37604 // render grid on panel activation (if panel background)
37605 this.on('activate', function(gp) {
37606 if (!gp.grid.rendered) {
37607 gp.grid.render(this.wrapper);
37608 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37613 this.grid.render(this.wrapper);
37614 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37617 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37618 // ??? needed ??? config.el = this.wrapper;
37623 // xtype created footer. - not sure if will work as we normally have to render first..
37624 if (this.footer && !this.footer.el && this.footer.xtype) {
37626 var ctr = this.grid.getView().getFooterPanel(true);
37627 this.footer.dataSource = this.grid.dataSource;
37628 this.footer = Roo.factory(this.footer, Roo);
37629 this.footer.render(ctr);
37639 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37640 getId : function(){
37641 return this.grid.id;
37645 * Returns the grid for this panel
37646 * @return {Roo.bootstrap.Table}
37648 getGrid : function(){
37652 setSize : function(width, height){
37653 if(!this.ignoreResize(width, height)){
37654 var grid = this.grid;
37655 var size = this.adjustForComponents(width, height);
37656 var gridel = grid.getGridEl();
37657 gridel.setSize(size.width, size.height);
37659 var thd = grid.getGridEl().select('thead',true).first();
37660 var tbd = grid.getGridEl().select('tbody', true).first();
37662 tbd.setSize(width, height - thd.getHeight());
37671 beforeSlide : function(){
37672 this.grid.getView().scroller.clip();
37675 afterSlide : function(){
37676 this.grid.getView().scroller.unclip();
37679 destroy : function(){
37680 this.grid.destroy();
37682 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37687 * @class Roo.bootstrap.panel.Nest
37688 * @extends Roo.bootstrap.panel.Content
37690 * Create a new Panel, that can contain a layout.Border.
37693 * @param {Roo.BorderLayout} layout The layout for this panel
37694 * @param {String/Object} config A string to set only the title or a config object
37696 Roo.bootstrap.panel.Nest = function(config)
37698 // construct with only one argument..
37699 /* FIXME - implement nicer consturctors
37700 if (layout.layout) {
37702 layout = config.layout;
37703 delete config.layout;
37705 if (layout.xtype && !layout.getEl) {
37706 // then layout needs constructing..
37707 layout = Roo.factory(layout, Roo);
37711 config.el = config.layout.getEl();
37713 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37715 config.layout.monitorWindowResize = false; // turn off autosizing
37716 this.layout = config.layout;
37717 this.layout.getEl().addClass("roo-layout-nested-layout");
37724 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37726 setSize : function(width, height){
37727 if(!this.ignoreResize(width, height)){
37728 var size = this.adjustForComponents(width, height);
37729 var el = this.layout.getEl();
37730 if (size.height < 1) {
37731 el.setWidth(size.width);
37733 el.setSize(size.width, size.height);
37735 var touch = el.dom.offsetWidth;
37736 this.layout.layout();
37737 // ie requires a double layout on the first pass
37738 if(Roo.isIE && !this.initialized){
37739 this.initialized = true;
37740 this.layout.layout();
37745 // activate all subpanels if not currently active..
37747 setActiveState : function(active){
37748 this.active = active;
37749 this.setActiveClass(active);
37752 this.fireEvent("deactivate", this);
37756 this.fireEvent("activate", this);
37757 // not sure if this should happen before or after..
37758 if (!this.layout) {
37759 return; // should not happen..
37762 for (var r in this.layout.regions) {
37763 reg = this.layout.getRegion(r);
37764 if (reg.getActivePanel()) {
37765 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37766 reg.setActivePanel(reg.getActivePanel());
37769 if (!reg.panels.length) {
37772 reg.showPanel(reg.getPanel(0));
37781 * Returns the nested BorderLayout for this panel
37782 * @return {Roo.BorderLayout}
37784 getLayout : function(){
37785 return this.layout;
37789 * Adds a xtype elements to the layout of the nested panel
37793 xtype : 'ContentPanel',
37800 xtype : 'NestedLayoutPanel',
37806 items : [ ... list of content panels or nested layout panels.. ]
37810 * @param {Object} cfg Xtype definition of item to add.
37812 addxtype : function(cfg) {
37813 return this.layout.addxtype(cfg);
37818 * Ext JS Library 1.1.1
37819 * Copyright(c) 2006-2007, Ext JS, LLC.
37821 * Originally Released Under LGPL - original licence link has changed is not relivant.
37824 * <script type="text/javascript">
37827 * @class Roo.TabPanel
37828 * @extends Roo.util.Observable
37829 * A lightweight tab container.
37833 // basic tabs 1, built from existing content
37834 var tabs = new Roo.TabPanel("tabs1");
37835 tabs.addTab("script", "View Script");
37836 tabs.addTab("markup", "View Markup");
37837 tabs.activate("script");
37839 // more advanced tabs, built from javascript
37840 var jtabs = new Roo.TabPanel("jtabs");
37841 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37843 // set up the UpdateManager
37844 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37845 var updater = tab2.getUpdateManager();
37846 updater.setDefaultUrl("ajax1.htm");
37847 tab2.on('activate', updater.refresh, updater, true);
37849 // Use setUrl for Ajax loading
37850 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37851 tab3.setUrl("ajax2.htm", null, true);
37854 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37857 jtabs.activate("jtabs-1");
37860 * Create a new TabPanel.
37861 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37862 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37864 Roo.bootstrap.panel.Tabs = function(config){
37866 * The container element for this TabPanel.
37867 * @type Roo.Element
37869 this.el = Roo.get(config.el);
37872 if(typeof config == "boolean"){
37873 this.tabPosition = config ? "bottom" : "top";
37875 Roo.apply(this, config);
37879 if(this.tabPosition == "bottom"){
37880 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37881 this.el.addClass("roo-tabs-bottom");
37883 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37884 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37885 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37887 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37889 if(this.tabPosition != "bottom"){
37890 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37891 * @type Roo.Element
37893 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37894 this.el.addClass("roo-tabs-top");
37898 this.bodyEl.setStyle("position", "relative");
37900 this.active = null;
37901 this.activateDelegate = this.activate.createDelegate(this);
37906 * Fires when the active tab changes
37907 * @param {Roo.TabPanel} this
37908 * @param {Roo.TabPanelItem} activePanel The new active tab
37912 * @event beforetabchange
37913 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37914 * @param {Roo.TabPanel} this
37915 * @param {Object} e Set cancel to true on this object to cancel the tab change
37916 * @param {Roo.TabPanelItem} tab The tab being changed to
37918 "beforetabchange" : true
37921 Roo.EventManager.onWindowResize(this.onResize, this);
37922 this.cpad = this.el.getPadding("lr");
37923 this.hiddenCount = 0;
37926 // toolbar on the tabbar support...
37927 if (this.toolbar) {
37928 alert("no toolbar support yet");
37929 this.toolbar = false;
37931 var tcfg = this.toolbar;
37932 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37933 this.toolbar = new Roo.Toolbar(tcfg);
37934 if (Roo.isSafari) {
37935 var tbl = tcfg.container.child('table', true);
37936 tbl.setAttribute('width', '100%');
37944 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37947 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37949 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37951 tabPosition : "top",
37953 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37955 currentTabWidth : 0,
37957 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37961 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37965 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37967 preferredTabWidth : 175,
37969 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37971 resizeTabs : false,
37973 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37975 monitorResize : true,
37977 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37982 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37983 * @param {String} id The id of the div to use <b>or create</b>
37984 * @param {String} text The text for the tab
37985 * @param {String} content (optional) Content to put in the TabPanelItem body
37986 * @param {Boolean} closable (optional) True to create a close icon on the tab
37987 * @return {Roo.TabPanelItem} The created TabPanelItem
37989 addTab : function(id, text, content, closable, tpl)
37991 var item = new Roo.bootstrap.panel.TabItem({
37995 closable : closable,
37998 this.addTabItem(item);
38000 item.setContent(content);
38006 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38007 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38008 * @return {Roo.TabPanelItem}
38010 getTab : function(id){
38011 return this.items[id];
38015 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38016 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38018 hideTab : function(id){
38019 var t = this.items[id];
38022 this.hiddenCount++;
38023 this.autoSizeTabs();
38028 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38029 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38031 unhideTab : function(id){
38032 var t = this.items[id];
38034 t.setHidden(false);
38035 this.hiddenCount--;
38036 this.autoSizeTabs();
38041 * Adds an existing {@link Roo.TabPanelItem}.
38042 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38044 addTabItem : function(item){
38045 this.items[item.id] = item;
38046 this.items.push(item);
38047 // if(this.resizeTabs){
38048 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38049 // this.autoSizeTabs();
38051 // item.autoSize();
38056 * Removes a {@link Roo.TabPanelItem}.
38057 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38059 removeTab : function(id){
38060 var items = this.items;
38061 var tab = items[id];
38062 if(!tab) { return; }
38063 var index = items.indexOf(tab);
38064 if(this.active == tab && items.length > 1){
38065 var newTab = this.getNextAvailable(index);
38070 this.stripEl.dom.removeChild(tab.pnode.dom);
38071 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38072 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38074 items.splice(index, 1);
38075 delete this.items[tab.id];
38076 tab.fireEvent("close", tab);
38077 tab.purgeListeners();
38078 this.autoSizeTabs();
38081 getNextAvailable : function(start){
38082 var items = this.items;
38084 // look for a next tab that will slide over to
38085 // replace the one being removed
38086 while(index < items.length){
38087 var item = items[++index];
38088 if(item && !item.isHidden()){
38092 // if one isn't found select the previous tab (on the left)
38095 var item = items[--index];
38096 if(item && !item.isHidden()){
38104 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38105 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38107 disableTab : function(id){
38108 var tab = this.items[id];
38109 if(tab && this.active != tab){
38115 * Enables a {@link Roo.TabPanelItem} that is disabled.
38116 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38118 enableTab : function(id){
38119 var tab = this.items[id];
38124 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38125 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38126 * @return {Roo.TabPanelItem} The TabPanelItem.
38128 activate : function(id){
38129 var tab = this.items[id];
38133 if(tab == this.active || tab.disabled){
38137 this.fireEvent("beforetabchange", this, e, tab);
38138 if(e.cancel !== true && !tab.disabled){
38140 this.active.hide();
38142 this.active = this.items[id];
38143 this.active.show();
38144 this.fireEvent("tabchange", this, this.active);
38150 * Gets the active {@link Roo.TabPanelItem}.
38151 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38153 getActiveTab : function(){
38154 return this.active;
38158 * Updates the tab body element to fit the height of the container element
38159 * for overflow scrolling
38160 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38162 syncHeight : function(targetHeight){
38163 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38164 var bm = this.bodyEl.getMargins();
38165 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38166 this.bodyEl.setHeight(newHeight);
38170 onResize : function(){
38171 if(this.monitorResize){
38172 this.autoSizeTabs();
38177 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38179 beginUpdate : function(){
38180 this.updating = true;
38184 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38186 endUpdate : function(){
38187 this.updating = false;
38188 this.autoSizeTabs();
38192 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38194 autoSizeTabs : function(){
38195 var count = this.items.length;
38196 var vcount = count - this.hiddenCount;
38197 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38200 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38201 var availWidth = Math.floor(w / vcount);
38202 var b = this.stripBody;
38203 if(b.getWidth() > w){
38204 var tabs = this.items;
38205 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38206 if(availWidth < this.minTabWidth){
38207 /*if(!this.sleft){ // incomplete scrolling code
38208 this.createScrollButtons();
38211 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38214 if(this.currentTabWidth < this.preferredTabWidth){
38215 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38221 * Returns the number of tabs in this TabPanel.
38224 getCount : function(){
38225 return this.items.length;
38229 * Resizes all the tabs to the passed width
38230 * @param {Number} The new width
38232 setTabWidth : function(width){
38233 this.currentTabWidth = width;
38234 for(var i = 0, len = this.items.length; i < len; i++) {
38235 if(!this.items[i].isHidden()) {
38236 this.items[i].setWidth(width);
38242 * Destroys this TabPanel
38243 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38245 destroy : function(removeEl){
38246 Roo.EventManager.removeResizeListener(this.onResize, this);
38247 for(var i = 0, len = this.items.length; i < len; i++){
38248 this.items[i].purgeListeners();
38250 if(removeEl === true){
38251 this.el.update("");
38256 createStrip : function(container)
38258 var strip = document.createElement("nav");
38259 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38260 container.appendChild(strip);
38264 createStripList : function(strip)
38266 // div wrapper for retard IE
38267 // returns the "tr" element.
38268 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38269 //'<div class="x-tabs-strip-wrap">'+
38270 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38271 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38272 return strip.firstChild; //.firstChild.firstChild.firstChild;
38274 createBody : function(container)
38276 var body = document.createElement("div");
38277 Roo.id(body, "tab-body");
38278 //Roo.fly(body).addClass("x-tabs-body");
38279 Roo.fly(body).addClass("tab-content");
38280 container.appendChild(body);
38283 createItemBody :function(bodyEl, id){
38284 var body = Roo.getDom(id);
38286 body = document.createElement("div");
38289 //Roo.fly(body).addClass("x-tabs-item-body");
38290 Roo.fly(body).addClass("tab-pane");
38291 bodyEl.insertBefore(body, bodyEl.firstChild);
38295 createStripElements : function(stripEl, text, closable, tpl)
38297 var td = document.createElement("li"); // was td..
38300 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38303 stripEl.appendChild(td);
38305 td.className = "x-tabs-closable";
38306 if(!this.closeTpl){
38307 this.closeTpl = new Roo.Template(
38308 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38309 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38310 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38313 var el = this.closeTpl.overwrite(td, {"text": text});
38314 var close = el.getElementsByTagName("div")[0];
38315 var inner = el.getElementsByTagName("em")[0];
38316 return {"el": el, "close": close, "inner": inner};
38319 // not sure what this is..
38320 // if(!this.tabTpl){
38321 //this.tabTpl = new Roo.Template(
38322 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38323 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38325 // this.tabTpl = new Roo.Template(
38326 // '<a href="#">' +
38327 // '<span unselectable="on"' +
38328 // (this.disableTooltips ? '' : ' title="{text}"') +
38329 // ' >{text}</span></a>'
38335 var template = tpl || this.tabTpl || false;
38339 template = new Roo.Template(
38341 '<span unselectable="on"' +
38342 (this.disableTooltips ? '' : ' title="{text}"') +
38343 ' >{text}</span></a>'
38347 switch (typeof(template)) {
38351 template = new Roo.Template(template);
38357 var el = template.overwrite(td, {"text": text});
38359 var inner = el.getElementsByTagName("span")[0];
38361 return {"el": el, "inner": inner};
38369 * @class Roo.TabPanelItem
38370 * @extends Roo.util.Observable
38371 * Represents an individual item (tab plus body) in a TabPanel.
38372 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38373 * @param {String} id The id of this TabPanelItem
38374 * @param {String} text The text for the tab of this TabPanelItem
38375 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38377 Roo.bootstrap.panel.TabItem = function(config){
38379 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38380 * @type Roo.TabPanel
38382 this.tabPanel = config.panel;
38384 * The id for this TabPanelItem
38387 this.id = config.id;
38389 this.disabled = false;
38391 this.text = config.text;
38393 this.loaded = false;
38394 this.closable = config.closable;
38397 * The body element for this TabPanelItem.
38398 * @type Roo.Element
38400 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38401 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38402 this.bodyEl.setStyle("display", "block");
38403 this.bodyEl.setStyle("zoom", "1");
38404 //this.hideAction();
38406 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38408 this.el = Roo.get(els.el);
38409 this.inner = Roo.get(els.inner, true);
38410 this.textEl = Roo.get(this.el.dom.firstChild, true);
38411 this.pnode = Roo.get(els.el.parentNode, true);
38412 // this.el.on("mousedown", this.onTabMouseDown, this);
38413 this.el.on("click", this.onTabClick, this);
38415 if(config.closable){
38416 var c = Roo.get(els.close, true);
38417 c.dom.title = this.closeText;
38418 c.addClassOnOver("close-over");
38419 c.on("click", this.closeClick, this);
38425 * Fires when this tab becomes the active tab.
38426 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38427 * @param {Roo.TabPanelItem} this
38431 * @event beforeclose
38432 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38433 * @param {Roo.TabPanelItem} this
38434 * @param {Object} e Set cancel to true on this object to cancel the close.
38436 "beforeclose": true,
38439 * Fires when this tab is closed.
38440 * @param {Roo.TabPanelItem} this
38444 * @event deactivate
38445 * Fires when this tab is no longer the active tab.
38446 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38447 * @param {Roo.TabPanelItem} this
38449 "deactivate" : true
38451 this.hidden = false;
38453 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38456 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38458 purgeListeners : function(){
38459 Roo.util.Observable.prototype.purgeListeners.call(this);
38460 this.el.removeAllListeners();
38463 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38466 this.pnode.addClass("active");
38469 this.tabPanel.stripWrap.repaint();
38471 this.fireEvent("activate", this.tabPanel, this);
38475 * Returns true if this tab is the active tab.
38476 * @return {Boolean}
38478 isActive : function(){
38479 return this.tabPanel.getActiveTab() == this;
38483 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38486 this.pnode.removeClass("active");
38488 this.fireEvent("deactivate", this.tabPanel, this);
38491 hideAction : function(){
38492 this.bodyEl.hide();
38493 this.bodyEl.setStyle("position", "absolute");
38494 this.bodyEl.setLeft("-20000px");
38495 this.bodyEl.setTop("-20000px");
38498 showAction : function(){
38499 this.bodyEl.setStyle("position", "relative");
38500 this.bodyEl.setTop("");
38501 this.bodyEl.setLeft("");
38502 this.bodyEl.show();
38506 * Set the tooltip for the tab.
38507 * @param {String} tooltip The tab's tooltip
38509 setTooltip : function(text){
38510 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38511 this.textEl.dom.qtip = text;
38512 this.textEl.dom.removeAttribute('title');
38514 this.textEl.dom.title = text;
38518 onTabClick : function(e){
38519 e.preventDefault();
38520 this.tabPanel.activate(this.id);
38523 onTabMouseDown : function(e){
38524 e.preventDefault();
38525 this.tabPanel.activate(this.id);
38528 getWidth : function(){
38529 return this.inner.getWidth();
38532 setWidth : function(width){
38533 var iwidth = width - this.pnode.getPadding("lr");
38534 this.inner.setWidth(iwidth);
38535 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38536 this.pnode.setWidth(width);
38540 * Show or hide the tab
38541 * @param {Boolean} hidden True to hide or false to show.
38543 setHidden : function(hidden){
38544 this.hidden = hidden;
38545 this.pnode.setStyle("display", hidden ? "none" : "");
38549 * Returns true if this tab is "hidden"
38550 * @return {Boolean}
38552 isHidden : function(){
38553 return this.hidden;
38557 * Returns the text for this tab
38560 getText : function(){
38564 autoSize : function(){
38565 //this.el.beginMeasure();
38566 this.textEl.setWidth(1);
38568 * #2804 [new] Tabs in Roojs
38569 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38571 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38572 //this.el.endMeasure();
38576 * Sets the text for the tab (Note: this also sets the tooltip text)
38577 * @param {String} text The tab's text and tooltip
38579 setText : function(text){
38581 this.textEl.update(text);
38582 this.setTooltip(text);
38583 //if(!this.tabPanel.resizeTabs){
38584 // this.autoSize();
38588 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38590 activate : function(){
38591 this.tabPanel.activate(this.id);
38595 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38597 disable : function(){
38598 if(this.tabPanel.active != this){
38599 this.disabled = true;
38600 this.pnode.addClass("disabled");
38605 * Enables this TabPanelItem if it was previously disabled.
38607 enable : function(){
38608 this.disabled = false;
38609 this.pnode.removeClass("disabled");
38613 * Sets the content for this TabPanelItem.
38614 * @param {String} content The content
38615 * @param {Boolean} loadScripts true to look for and load scripts
38617 setContent : function(content, loadScripts){
38618 this.bodyEl.update(content, loadScripts);
38622 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38623 * @return {Roo.UpdateManager} The UpdateManager
38625 getUpdateManager : function(){
38626 return this.bodyEl.getUpdateManager();
38630 * Set a URL to be used to load the content for this TabPanelItem.
38631 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38632 * @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)
38633 * @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)
38634 * @return {Roo.UpdateManager} The UpdateManager
38636 setUrl : function(url, params, loadOnce){
38637 if(this.refreshDelegate){
38638 this.un('activate', this.refreshDelegate);
38640 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38641 this.on("activate", this.refreshDelegate);
38642 return this.bodyEl.getUpdateManager();
38646 _handleRefresh : function(url, params, loadOnce){
38647 if(!loadOnce || !this.loaded){
38648 var updater = this.bodyEl.getUpdateManager();
38649 updater.update(url, params, this._setLoaded.createDelegate(this));
38654 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38655 * Will fail silently if the setUrl method has not been called.
38656 * This does not activate the panel, just updates its content.
38658 refresh : function(){
38659 if(this.refreshDelegate){
38660 this.loaded = false;
38661 this.refreshDelegate();
38666 _setLoaded : function(){
38667 this.loaded = true;
38671 closeClick : function(e){
38674 this.fireEvent("beforeclose", this, o);
38675 if(o.cancel !== true){
38676 this.tabPanel.removeTab(this.id);
38680 * The text displayed in the tooltip for the close icon.
38683 closeText : "Close this tab"
38686 * This script refer to:
38687 * Title: International Telephone Input
38688 * Author: Jack O'Connor
38689 * Code version: v12.1.12
38690 * Availability: https://github.com/jackocnr/intl-tel-input.git
38693 Roo.bootstrap.PhoneInputData = function() {
38696 "Afghanistan (افغانستان)",
38701 "Albania (Shqipëri)",
38706 "Algeria (الجزائر)",
38731 "Antigua and Barbuda",
38741 "Armenia (Հայաստան)",
38757 "Austria (Österreich)",
38762 "Azerbaijan (Azərbaycan)",
38772 "Bahrain (البحرين)",
38777 "Bangladesh (বাংলাদেশ)",
38787 "Belarus (Беларусь)",
38792 "Belgium (België)",
38822 "Bosnia and Herzegovina (Босна и Херцеговина)",
38837 "British Indian Ocean Territory",
38842 "British Virgin Islands",
38852 "Bulgaria (България)",
38862 "Burundi (Uburundi)",
38867 "Cambodia (កម្ពុជា)",
38872 "Cameroon (Cameroun)",
38881 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38884 "Cape Verde (Kabu Verdi)",
38889 "Caribbean Netherlands",
38900 "Central African Republic (République centrafricaine)",
38920 "Christmas Island",
38926 "Cocos (Keeling) Islands",
38937 "Comoros (جزر القمر)",
38942 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38947 "Congo (Republic) (Congo-Brazzaville)",
38967 "Croatia (Hrvatska)",
38988 "Czech Republic (Česká republika)",
38993 "Denmark (Danmark)",
39008 "Dominican Republic (República Dominicana)",
39012 ["809", "829", "849"]
39030 "Equatorial Guinea (Guinea Ecuatorial)",
39050 "Falkland Islands (Islas Malvinas)",
39055 "Faroe Islands (Føroyar)",
39076 "French Guiana (Guyane française)",
39081 "French Polynesia (Polynésie française)",
39096 "Georgia (საქართველო)",
39101 "Germany (Deutschland)",
39121 "Greenland (Kalaallit Nunaat)",
39158 "Guinea-Bissau (Guiné Bissau)",
39183 "Hungary (Magyarország)",
39188 "Iceland (Ísland)",
39208 "Iraq (العراق)",
39224 "Israel (ישראל)",
39251 "Jordan (الأردن)",
39256 "Kazakhstan (Казахстан)",
39277 "Kuwait (الكويت)",
39282 "Kyrgyzstan (Кыргызстан)",
39292 "Latvia (Latvija)",
39297 "Lebanon (لبنان)",
39312 "Libya (ليبيا)",
39322 "Lithuania (Lietuva)",
39337 "Macedonia (FYROM) (Македонија)",
39342 "Madagascar (Madagasikara)",
39372 "Marshall Islands",
39382 "Mauritania (موريتانيا)",
39387 "Mauritius (Moris)",
39408 "Moldova (Republica Moldova)",
39418 "Mongolia (Монгол)",
39423 "Montenegro (Crna Gora)",
39433 "Morocco (المغرب)",
39439 "Mozambique (Moçambique)",
39444 "Myanmar (Burma) (မြန်မာ)",
39449 "Namibia (Namibië)",
39464 "Netherlands (Nederland)",
39469 "New Caledonia (Nouvelle-Calédonie)",
39504 "North Korea (조선 민주주의 인민 공화국)",
39509 "Northern Mariana Islands",
39525 "Pakistan (پاکستان)",
39535 "Palestine (فلسطين)",
39545 "Papua New Guinea",
39587 "Réunion (La Réunion)",
39593 "Romania (România)",
39609 "Saint Barthélemy",
39620 "Saint Kitts and Nevis",
39630 "Saint Martin (Saint-Martin (partie française))",
39636 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39641 "Saint Vincent and the Grenadines",
39656 "São Tomé and Príncipe (São Tomé e Príncipe)",
39661 "Saudi Arabia (المملكة العربية السعودية)",
39666 "Senegal (Sénégal)",
39696 "Slovakia (Slovensko)",
39701 "Slovenia (Slovenija)",
39711 "Somalia (Soomaaliya)",
39721 "South Korea (대한민국)",
39726 "South Sudan (جنوب السودان)",
39736 "Sri Lanka (ශ්රී ලංකාව)",
39741 "Sudan (السودان)",
39751 "Svalbard and Jan Mayen",
39762 "Sweden (Sverige)",
39767 "Switzerland (Schweiz)",
39772 "Syria (سوريا)",
39817 "Trinidad and Tobago",
39822 "Tunisia (تونس)",
39827 "Turkey (Türkiye)",
39837 "Turks and Caicos Islands",
39847 "U.S. Virgin Islands",
39857 "Ukraine (Україна)",
39862 "United Arab Emirates (الإمارات العربية المتحدة)",
39884 "Uzbekistan (Oʻzbekiston)",
39894 "Vatican City (Città del Vaticano)",
39905 "Vietnam (Việt Nam)",
39910 "Wallis and Futuna (Wallis-et-Futuna)",
39915 "Western Sahara (الصحراء الغربية)",
39921 "Yemen (اليمن)",
39945 * This script refer to:
39946 * Title: International Telephone Input
39947 * Author: Jack O'Connor
39948 * Code version: v12.1.12
39949 * Availability: https://github.com/jackocnr/intl-tel-input.git
39953 * @class Roo.bootstrap.PhoneInput
39954 * @extends Roo.bootstrap.TriggerField
39955 * An input with International dial-code selection
39957 * @cfg {String} defaultDialCode default '+852'
39958 * @cfg {Array} preferedCountries default []
39961 * Create a new PhoneInput.
39962 * @param {Object} config Configuration options
39965 Roo.bootstrap.PhoneInput = function(config) {
39966 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39969 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39971 listWidth: undefined,
39973 selectedClass: 'active',
39975 invalidClass : "has-warning",
39977 validClass: 'has-success',
39979 allowed: '0123456789',
39984 * @cfg {String} defaultDialCode The default dial code when initializing the input
39986 defaultDialCode: '+852',
39989 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39991 preferedCountries: false,
39993 getAutoCreate : function()
39995 var data = Roo.bootstrap.PhoneInputData();
39996 var align = this.labelAlign || this.parentLabelAlign();
39999 this.allCountries = [];
40000 this.dialCodeMapping = [];
40002 for (var i = 0; i < data.length; i++) {
40004 this.allCountries[i] = {
40008 priority: c[3] || 0,
40009 areaCodes: c[4] || null
40011 this.dialCodeMapping[c[2]] = {
40014 priority: c[3] || 0,
40015 areaCodes: c[4] || null
40027 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40028 maxlength: this.max_length,
40029 cls : 'form-control tel-input',
40030 autocomplete: 'new-password'
40033 var hiddenInput = {
40036 cls: 'hidden-tel-input'
40040 hiddenInput.name = this.name;
40043 if (this.disabled) {
40044 input.disabled = true;
40047 var flag_container = {
40064 cls: this.hasFeedback ? 'has-feedback' : '',
40070 cls: 'dial-code-holder',
40077 cls: 'roo-select2-container input-group',
40084 if (this.fieldLabel.length) {
40087 tooltip: 'This field is required'
40093 cls: 'control-label',
40099 html: this.fieldLabel
40102 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40108 if(this.indicatorpos == 'right') {
40109 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40116 if(align == 'left') {
40124 if(this.labelWidth > 12){
40125 label.style = "width: " + this.labelWidth + 'px';
40127 if(this.labelWidth < 13 && this.labelmd == 0){
40128 this.labelmd = this.labelWidth;
40130 if(this.labellg > 0){
40131 label.cls += ' col-lg-' + this.labellg;
40132 input.cls += ' col-lg-' + (12 - this.labellg);
40134 if(this.labelmd > 0){
40135 label.cls += ' col-md-' + this.labelmd;
40136 container.cls += ' col-md-' + (12 - this.labelmd);
40138 if(this.labelsm > 0){
40139 label.cls += ' col-sm-' + this.labelsm;
40140 container.cls += ' col-sm-' + (12 - this.labelsm);
40142 if(this.labelxs > 0){
40143 label.cls += ' col-xs-' + this.labelxs;
40144 container.cls += ' col-xs-' + (12 - this.labelxs);
40154 var settings = this;
40156 ['xs','sm','md','lg'].map(function(size){
40157 if (settings[size]) {
40158 cfg.cls += ' col-' + size + '-' + settings[size];
40162 this.store = new Roo.data.Store({
40163 proxy : new Roo.data.MemoryProxy({}),
40164 reader : new Roo.data.JsonReader({
40175 'name' : 'dialCode',
40179 'name' : 'priority',
40183 'name' : 'areaCodes',
40190 if(!this.preferedCountries) {
40191 this.preferedCountries = [
40198 var p = this.preferedCountries.reverse();
40201 for (var i = 0; i < p.length; i++) {
40202 for (var j = 0; j < this.allCountries.length; j++) {
40203 if(this.allCountries[j].iso2 == p[i]) {
40204 var t = this.allCountries[j];
40205 this.allCountries.splice(j,1);
40206 this.allCountries.unshift(t);
40212 this.store.proxy.data = {
40214 data: this.allCountries
40220 initEvents : function()
40223 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40225 this.indicator = this.indicatorEl();
40226 this.flag = this.flagEl();
40227 this.dialCodeHolder = this.dialCodeHolderEl();
40229 this.trigger = this.el.select('div.flag-box',true).first();
40230 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40235 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40236 _this.list.setWidth(lw);
40239 this.list.on('mouseover', this.onViewOver, this);
40240 this.list.on('mousemove', this.onViewMove, this);
40241 this.inputEl().on("keyup", this.onKeyUp, this);
40242 this.inputEl().on("keypress", this.onKeyPress, this);
40244 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40246 this.view = new Roo.View(this.list, this.tpl, {
40247 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40250 this.view.on('click', this.onViewClick, this);
40251 this.setValue(this.defaultDialCode);
40254 onTriggerClick : function(e)
40256 Roo.log('trigger click');
40261 if(this.isExpanded()){
40263 this.hasFocus = false;
40265 this.store.load({});
40266 this.hasFocus = true;
40271 isExpanded : function()
40273 return this.list.isVisible();
40276 collapse : function()
40278 if(!this.isExpanded()){
40282 Roo.get(document).un('mousedown', this.collapseIf, this);
40283 Roo.get(document).un('mousewheel', this.collapseIf, this);
40284 this.fireEvent('collapse', this);
40288 expand : function()
40292 if(this.isExpanded() || !this.hasFocus){
40296 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40297 this.list.setWidth(lw);
40300 this.restrictHeight();
40302 Roo.get(document).on('mousedown', this.collapseIf, this);
40303 Roo.get(document).on('mousewheel', this.collapseIf, this);
40305 this.fireEvent('expand', this);
40308 restrictHeight : function()
40310 this.list.alignTo(this.inputEl(), this.listAlign);
40311 this.list.alignTo(this.inputEl(), this.listAlign);
40314 onViewOver : function(e, t)
40316 if(this.inKeyMode){
40319 var item = this.view.findItemFromChild(t);
40322 var index = this.view.indexOf(item);
40323 this.select(index, false);
40328 onViewClick : function(view, doFocus, el, e)
40330 var index = this.view.getSelectedIndexes()[0];
40332 var r = this.store.getAt(index);
40335 this.onSelect(r, index);
40337 if(doFocus !== false && !this.blockFocus){
40338 this.inputEl().focus();
40342 onViewMove : function(e, t)
40344 this.inKeyMode = false;
40347 select : function(index, scrollIntoView)
40349 this.selectedIndex = index;
40350 this.view.select(index);
40351 if(scrollIntoView !== false){
40352 var el = this.view.getNode(index);
40354 this.list.scrollChildIntoView(el, false);
40359 createList : function()
40361 this.list = Roo.get(document.body).createChild({
40363 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40364 style: 'display:none'
40367 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40370 collapseIf : function(e)
40372 var in_combo = e.within(this.el);
40373 var in_list = e.within(this.list);
40374 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40376 if (in_combo || in_list || is_list) {
40382 onSelect : function(record, index)
40384 if(this.fireEvent('beforeselect', this, record, index) !== false){
40386 this.setFlagClass(record.data.iso2);
40387 this.setDialCode(record.data.dialCode);
40388 this.hasFocus = false;
40390 this.fireEvent('select', this, record, index);
40394 flagEl : function()
40396 var flag = this.el.select('div.flag',true).first();
40403 dialCodeHolderEl : function()
40405 var d = this.el.select('input.dial-code-holder',true).first();
40412 setDialCode : function(v)
40414 this.dialCodeHolder.dom.value = '+'+v;
40417 setFlagClass : function(n)
40419 this.flag.dom.className = 'flag '+n;
40422 getValue : function()
40424 var v = this.inputEl().getValue();
40425 if(this.dialCodeHolder) {
40426 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40431 setValue : function(v)
40433 var d = this.getDialCode(v);
40435 //invalid dial code
40436 if(v.length == 0 || !d || d.length == 0) {
40438 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40439 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40445 this.setFlagClass(this.dialCodeMapping[d].iso2);
40446 this.setDialCode(d);
40447 this.inputEl().dom.value = v.replace('+'+d,'');
40448 this.hiddenEl().dom.value = this.getValue();
40453 getDialCode : function(v)
40457 if (v.length == 0) {
40458 return this.dialCodeHolder.dom.value;
40462 if (v.charAt(0) != "+") {
40465 var numericChars = "";
40466 for (var i = 1; i < v.length; i++) {
40467 var c = v.charAt(i);
40470 if (this.dialCodeMapping[numericChars]) {
40471 dialCode = v.substr(1, i);
40473 if (numericChars.length == 4) {
40483 this.setValue(this.defaultDialCode);
40487 hiddenEl : function()
40489 return this.el.select('input.hidden-tel-input',true).first();
40492 // after setting val
40493 onKeyUp : function(e){
40494 this.setValue(this.getValue());
40497 onKeyPress : function(e){
40498 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40505 * @class Roo.bootstrap.MoneyField
40506 * @extends Roo.bootstrap.ComboBox
40507 * Bootstrap MoneyField class
40510 * Create a new MoneyField.
40511 * @param {Object} config Configuration options
40514 Roo.bootstrap.MoneyField = function(config) {
40516 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40520 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40523 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40525 allowDecimals : true,
40527 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40529 decimalSeparator : ".",
40531 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40533 decimalPrecision : 0,
40535 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40537 allowNegative : true,
40539 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40543 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40545 minValue : Number.NEGATIVE_INFINITY,
40547 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40549 maxValue : Number.MAX_VALUE,
40551 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40553 minText : "The minimum value for this field is {0}",
40555 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40557 maxText : "The maximum value for this field is {0}",
40559 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40560 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40562 nanText : "{0} is not a valid number",
40564 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40568 * @cfg {String} defaults currency of the MoneyField
40569 * value should be in lkey
40571 defaultCurrency : false,
40573 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40575 thousandsDelimiter : false,
40577 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40588 getAutoCreate : function()
40590 var align = this.labelAlign || this.parentLabelAlign();
40602 cls : 'form-control roo-money-amount-input',
40603 autocomplete: 'new-password'
40606 var hiddenInput = {
40610 cls: 'hidden-number-input'
40613 if(this.max_length) {
40614 input.maxlength = this.max_length;
40618 hiddenInput.name = this.name;
40621 if (this.disabled) {
40622 input.disabled = true;
40625 var clg = 12 - this.inputlg;
40626 var cmd = 12 - this.inputmd;
40627 var csm = 12 - this.inputsm;
40628 var cxs = 12 - this.inputxs;
40632 cls : 'row roo-money-field',
40636 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40640 cls: 'roo-select2-container input-group',
40644 cls : 'form-control roo-money-currency-input',
40645 autocomplete: 'new-password',
40647 name : this.currencyName
40651 cls : 'input-group-addon',
40665 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40669 cls: this.hasFeedback ? 'has-feedback' : '',
40680 if (this.fieldLabel.length) {
40683 tooltip: 'This field is required'
40689 cls: 'control-label',
40695 html: this.fieldLabel
40698 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40704 if(this.indicatorpos == 'right') {
40705 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40712 if(align == 'left') {
40720 if(this.labelWidth > 12){
40721 label.style = "width: " + this.labelWidth + 'px';
40723 if(this.labelWidth < 13 && this.labelmd == 0){
40724 this.labelmd = this.labelWidth;
40726 if(this.labellg > 0){
40727 label.cls += ' col-lg-' + this.labellg;
40728 input.cls += ' col-lg-' + (12 - this.labellg);
40730 if(this.labelmd > 0){
40731 label.cls += ' col-md-' + this.labelmd;
40732 container.cls += ' col-md-' + (12 - this.labelmd);
40734 if(this.labelsm > 0){
40735 label.cls += ' col-sm-' + this.labelsm;
40736 container.cls += ' col-sm-' + (12 - this.labelsm);
40738 if(this.labelxs > 0){
40739 label.cls += ' col-xs-' + this.labelxs;
40740 container.cls += ' col-xs-' + (12 - this.labelxs);
40751 var settings = this;
40753 ['xs','sm','md','lg'].map(function(size){
40754 if (settings[size]) {
40755 cfg.cls += ' col-' + size + '-' + settings[size];
40762 initEvents : function()
40764 this.indicator = this.indicatorEl();
40766 this.initCurrencyEvent();
40768 this.initNumberEvent();
40771 initCurrencyEvent : function()
40774 throw "can not find store for combo";
40777 this.store = Roo.factory(this.store, Roo.data);
40778 this.store.parent = this;
40782 this.triggerEl = this.el.select('.input-group-addon', true).first();
40784 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40789 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40790 _this.list.setWidth(lw);
40793 this.list.on('mouseover', this.onViewOver, this);
40794 this.list.on('mousemove', this.onViewMove, this);
40795 this.list.on('scroll', this.onViewScroll, this);
40798 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40801 this.view = new Roo.View(this.list, this.tpl, {
40802 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40805 this.view.on('click', this.onViewClick, this);
40807 this.store.on('beforeload', this.onBeforeLoad, this);
40808 this.store.on('load', this.onLoad, this);
40809 this.store.on('loadexception', this.onLoadException, this);
40811 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40812 "up" : function(e){
40813 this.inKeyMode = true;
40817 "down" : function(e){
40818 if(!this.isExpanded()){
40819 this.onTriggerClick();
40821 this.inKeyMode = true;
40826 "enter" : function(e){
40829 if(this.fireEvent("specialkey", this, e)){
40830 this.onViewClick(false);
40836 "esc" : function(e){
40840 "tab" : function(e){
40843 if(this.fireEvent("specialkey", this, e)){
40844 this.onViewClick(false);
40852 doRelay : function(foo, bar, hname){
40853 if(hname == 'down' || this.scope.isExpanded()){
40854 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40862 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40866 initNumberEvent : function(e)
40868 this.inputEl().on("keydown" , this.fireKey, this);
40869 this.inputEl().on("focus", this.onFocus, this);
40870 this.inputEl().on("blur", this.onBlur, this);
40872 this.inputEl().relayEvent('keyup', this);
40874 if(this.indicator){
40875 this.indicator.addClass('invisible');
40878 this.originalValue = this.getValue();
40880 if(this.validationEvent == 'keyup'){
40881 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40882 this.inputEl().on('keyup', this.filterValidation, this);
40884 else if(this.validationEvent !== false){
40885 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40888 if(this.selectOnFocus){
40889 this.on("focus", this.preFocus, this);
40892 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40893 this.inputEl().on("keypress", this.filterKeys, this);
40895 this.inputEl().relayEvent('keypress', this);
40898 var allowed = "0123456789";
40900 if(this.allowDecimals){
40901 allowed += this.decimalSeparator;
40904 if(this.allowNegative){
40908 if(this.thousandsDelimiter) {
40912 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40914 var keyPress = function(e){
40916 var k = e.getKey();
40918 var c = e.getCharCode();
40921 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40922 allowed.indexOf(String.fromCharCode(c)) === -1
40928 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40932 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40937 this.inputEl().on("keypress", keyPress, this);
40941 onTriggerClick : function(e)
40948 this.loadNext = false;
40950 if(this.isExpanded()){
40955 this.hasFocus = true;
40957 if(this.triggerAction == 'all') {
40958 this.doQuery(this.allQuery, true);
40962 this.doQuery(this.getRawValue());
40965 getCurrency : function()
40967 var v = this.currencyEl().getValue();
40972 restrictHeight : function()
40974 this.list.alignTo(this.currencyEl(), this.listAlign);
40975 this.list.alignTo(this.currencyEl(), this.listAlign);
40978 onViewClick : function(view, doFocus, el, e)
40980 var index = this.view.getSelectedIndexes()[0];
40982 var r = this.store.getAt(index);
40985 this.onSelect(r, index);
40989 onSelect : function(record, index){
40991 if(this.fireEvent('beforeselect', this, record, index) !== false){
40993 this.setFromCurrencyData(index > -1 ? record.data : false);
40997 this.fireEvent('select', this, record, index);
41001 setFromCurrencyData : function(o)
41005 this.lastCurrency = o;
41007 if (this.currencyField) {
41008 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41010 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41013 this.lastSelectionText = currency;
41015 //setting default currency
41016 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41017 this.setCurrency(this.defaultCurrency);
41021 this.setCurrency(currency);
41024 setFromData : function(o)
41028 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41030 this.setFromCurrencyData(c);
41035 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41037 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41040 this.setValue(value);
41044 setCurrency : function(v)
41046 this.currencyValue = v;
41049 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41054 setValue : function(v)
41056 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41062 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41064 this.inputEl().dom.value = (v == '') ? '' :
41065 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41067 if(!this.allowZero && v === '0') {
41068 this.hiddenEl().dom.value = '';
41069 this.inputEl().dom.value = '';
41076 getRawValue : function()
41078 var v = this.inputEl().getValue();
41083 getValue : function()
41085 return this.fixPrecision(this.parseValue(this.getRawValue()));
41088 parseValue : function(value)
41090 if(this.thousandsDelimiter) {
41092 r = new RegExp(",", "g");
41093 value = value.replace(r, "");
41096 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41097 return isNaN(value) ? '' : value;
41101 fixPrecision : function(value)
41103 if(this.thousandsDelimiter) {
41105 r = new RegExp(",", "g");
41106 value = value.replace(r, "");
41109 var nan = isNaN(value);
41111 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41112 return nan ? '' : value;
41114 return parseFloat(value).toFixed(this.decimalPrecision);
41117 decimalPrecisionFcn : function(v)
41119 return Math.floor(v);
41122 validateValue : function(value)
41124 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41128 var num = this.parseValue(value);
41131 this.markInvalid(String.format(this.nanText, value));
41135 if(num < this.minValue){
41136 this.markInvalid(String.format(this.minText, this.minValue));
41140 if(num > this.maxValue){
41141 this.markInvalid(String.format(this.maxText, this.maxValue));
41148 validate : function()
41150 if(this.disabled || this.allowBlank){
41155 var currency = this.getCurrency();
41157 if(this.validateValue(this.getRawValue()) && currency.length){
41162 this.markInvalid();
41166 getName: function()
41171 beforeBlur : function()
41177 var v = this.parseValue(this.getRawValue());
41184 onBlur : function()
41188 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41189 //this.el.removeClass(this.focusClass);
41192 this.hasFocus = false;
41194 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41198 var v = this.getValue();
41200 if(String(v) !== String(this.startValue)){
41201 this.fireEvent('change', this, v, this.startValue);
41204 this.fireEvent("blur", this);
41207 inputEl : function()
41209 return this.el.select('.roo-money-amount-input', true).first();
41212 currencyEl : function()
41214 return this.el.select('.roo-money-currency-input', true).first();
41217 hiddenEl : function()
41219 return this.el.select('input.hidden-number-input',true).first();