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 this.el.select('.navbar-collapse',true).toggleClass('in');
3865 this.el.select('.navbar-collapse',true).toggleClass('collapse');
3875 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3877 var size = this.el.getSize();
3878 this.maskEl.setSize(size.width, size.height);
3879 this.maskEl.enableDisplayMode("block");
3888 getChildContainer : function()
3890 if (this.el.select('.collapse').getCount()) {
3891 return this.el.select('.collapse',true).first();
3924 * @class Roo.bootstrap.NavSimplebar
3925 * @extends Roo.bootstrap.Navbar
3926 * Bootstrap Sidebar class
3928 * @cfg {Boolean} inverse is inverted color
3930 * @cfg {String} type (nav | pills | tabs)
3931 * @cfg {Boolean} arrangement stacked | justified
3932 * @cfg {String} align (left | right) alignment
3934 * @cfg {Boolean} main (true|false) main nav bar? default false
3935 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3937 * @cfg {String} tag (header|footer|nav|div) default is nav
3939 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3943 * Create a new Sidebar
3944 * @param {Object} config The config object
3948 Roo.bootstrap.NavSimplebar = function(config){
3949 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3952 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3968 getAutoCreate : function(){
3972 tag : this.tag || 'div',
3973 cls : 'navbar navbar-expand-lg'
3975 if (['light','white'].indexOf(this.weight) > -1) {
3976 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3978 cfg.cls += ' bg-' + this.weight;
3990 this.type = this.type || 'nav';
3991 if (['tabs','pills'].indexOf(this.type)!==-1) {
3992 cfg.cn[0].cls += ' nav-' + this.type
3996 if (this.type!=='nav') {
3997 Roo.log('nav type must be nav/tabs/pills')
3999 cfg.cn[0].cls += ' navbar-nav'
4005 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4006 cfg.cn[0].cls += ' nav-' + this.arrangement;
4010 if (this.align === 'right') {
4011 cfg.cn[0].cls += ' navbar-right';
4015 cfg.cls += ' navbar-inverse';
4039 * navbar-expand-md fixed-top
4043 * @class Roo.bootstrap.NavHeaderbar
4044 * @extends Roo.bootstrap.NavSimplebar
4045 * Bootstrap Sidebar class
4047 * @cfg {String} brand what is brand
4048 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4049 * @cfg {String} brand_href href of the brand
4050 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4051 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4052 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4053 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4056 * Create a new Sidebar
4057 * @param {Object} config The config object
4061 Roo.bootstrap.NavHeaderbar = function(config){
4062 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4066 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4073 desktopCenter : false,
4076 getAutoCreate : function(){
4079 tag: this.nav || 'nav',
4080 cls: 'navbar navbar-expand-md',
4086 if (this.desktopCenter) {
4087 cn.push({cls : 'container', cn : []});
4095 cls: 'navbar-toggle navbar-toggler',
4096 'data-toggle': 'collapse',
4101 html: 'Toggle navigation'
4105 cls: 'icon-bar navbar-toggler-icon'
4118 cn.push( Roo.bootstrap.version == 4 ? btn : {
4120 cls: 'navbar-header',
4129 cls: 'collapse navbar-collapse',
4133 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4135 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4136 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4138 // tag can override this..
4140 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4143 if (this.brand !== '') {
4144 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4145 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4147 href: this.brand_href ? this.brand_href : '#',
4148 cls: 'navbar-brand',
4156 cfg.cls += ' main-nav';
4164 getHeaderChildContainer : function()
4166 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4167 return this.el.select('.navbar-header',true).first();
4170 return this.getChildContainer();
4174 initEvents : function()
4176 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4178 if (this.autohide) {
4183 Roo.get(document).on('scroll',function(e) {
4184 var ns = Roo.get(document).getScroll().top;
4185 var os = prevScroll;
4189 ft.removeClass('slideDown');
4190 ft.addClass('slideUp');
4193 ft.removeClass('slideUp');
4194 ft.addClass('slideDown');
4215 * @class Roo.bootstrap.NavSidebar
4216 * @extends Roo.bootstrap.Navbar
4217 * Bootstrap Sidebar class
4220 * Create a new Sidebar
4221 * @param {Object} config The config object
4225 Roo.bootstrap.NavSidebar = function(config){
4226 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4229 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4231 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4233 getAutoCreate : function(){
4238 cls: 'sidebar sidebar-nav'
4260 * @class Roo.bootstrap.NavGroup
4261 * @extends Roo.bootstrap.Component
4262 * Bootstrap NavGroup class
4263 * @cfg {String} align (left|right)
4264 * @cfg {Boolean} inverse
4265 * @cfg {String} type (nav|pills|tab) default nav
4266 * @cfg {String} navId - reference Id for navbar.
4270 * Create a new nav group
4271 * @param {Object} config The config object
4274 Roo.bootstrap.NavGroup = function(config){
4275 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4278 Roo.bootstrap.NavGroup.register(this);
4282 * Fires when the active item changes
4283 * @param {Roo.bootstrap.NavGroup} this
4284 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4285 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4292 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4303 getAutoCreate : function()
4305 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4312 if (['tabs','pills'].indexOf(this.type)!==-1) {
4313 cfg.cls += ' nav-' + this.type
4315 if (this.type!=='nav') {
4316 Roo.log('nav type must be nav/tabs/pills')
4318 cfg.cls += ' navbar-nav'
4321 if (this.parent() && this.parent().sidebar) {
4324 cls: 'dashboard-menu sidebar-menu'
4330 if (this.form === true) {
4336 if (this.align === 'right') {
4337 cfg.cls += ' navbar-right ml-md-auto';
4339 cfg.cls += ' navbar-left';
4343 if (this.align === 'right') {
4344 cfg.cls += ' navbar-right ml-md-auto';
4346 cfg.cls += ' mr-auto';
4350 cfg.cls += ' navbar-inverse';
4358 * sets the active Navigation item
4359 * @param {Roo.bootstrap.NavItem} the new current navitem
4361 setActiveItem : function(item)
4364 Roo.each(this.navItems, function(v){
4369 v.setActive(false, true);
4376 item.setActive(true, true);
4377 this.fireEvent('changed', this, item, prev);
4382 * gets the active Navigation item
4383 * @return {Roo.bootstrap.NavItem} the current navitem
4385 getActive : function()
4389 Roo.each(this.navItems, function(v){
4400 indexOfNav : function()
4404 Roo.each(this.navItems, function(v,i){
4415 * adds a Navigation item
4416 * @param {Roo.bootstrap.NavItem} the navitem to add
4418 addItem : function(cfg)
4420 var cn = new Roo.bootstrap.NavItem(cfg);
4422 cn.parentId = this.id;
4423 cn.onRender(this.el, null);
4427 * register a Navigation item
4428 * @param {Roo.bootstrap.NavItem} the navitem to add
4430 register : function(item)
4432 this.navItems.push( item);
4433 item.navId = this.navId;
4438 * clear all the Navigation item
4441 clearAll : function()
4444 this.el.dom.innerHTML = '';
4447 getNavItem: function(tabId)
4450 Roo.each(this.navItems, function(e) {
4451 if (e.tabId == tabId) {
4461 setActiveNext : function()
4463 var i = this.indexOfNav(this.getActive());
4464 if (i > this.navItems.length) {
4467 this.setActiveItem(this.navItems[i+1]);
4469 setActivePrev : function()
4471 var i = this.indexOfNav(this.getActive());
4475 this.setActiveItem(this.navItems[i-1]);
4477 clearWasActive : function(except) {
4478 Roo.each(this.navItems, function(e) {
4479 if (e.tabId != except.tabId && e.was_active) {
4480 e.was_active = false;
4487 getWasActive : function ()
4490 Roo.each(this.navItems, function(e) {
4505 Roo.apply(Roo.bootstrap.NavGroup, {
4509 * register a Navigation Group
4510 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4512 register : function(navgrp)
4514 this.groups[navgrp.navId] = navgrp;
4518 * fetch a Navigation Group based on the navigation ID
4519 * @param {string} the navgroup to add
4520 * @returns {Roo.bootstrap.NavGroup} the navgroup
4522 get: function(navId) {
4523 if (typeof(this.groups[navId]) == 'undefined') {
4525 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4527 return this.groups[navId] ;
4542 * @class Roo.bootstrap.NavItem
4543 * @extends Roo.bootstrap.Component
4544 * Bootstrap Navbar.NavItem class
4545 * @cfg {String} href link to
4546 * @cfg {String} html content of button
4547 * @cfg {String} badge text inside badge
4548 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4549 * @cfg {String} glyphicon DEPRICATED - use fa
4550 * @cfg {String} icon DEPRICATED - use fa
4551 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4552 * @cfg {Boolean} active Is item active
4553 * @cfg {Boolean} disabled Is item disabled
4555 * @cfg {Boolean} preventDefault (true | false) default false
4556 * @cfg {String} tabId the tab that this item activates.
4557 * @cfg {String} tagtype (a|span) render as a href or span?
4558 * @cfg {Boolean} animateRef (true|false) link to element default false
4561 * Create a new Navbar Item
4562 * @param {Object} config The config object
4564 Roo.bootstrap.NavItem = function(config){
4565 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4570 * The raw click event for the entire grid.
4571 * @param {Roo.EventObject} e
4576 * Fires when the active item active state changes
4577 * @param {Roo.bootstrap.NavItem} this
4578 * @param {boolean} state the new state
4584 * Fires when scroll to element
4585 * @param {Roo.bootstrap.NavItem} this
4586 * @param {Object} options
4587 * @param {Roo.EventObject} e
4595 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4604 preventDefault : false,
4611 getAutoCreate : function(){
4620 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4622 if (this.disabled) {
4623 cfg.cls += ' disabled';
4626 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4630 href : this.href || "#",
4631 html: this.html || ''
4634 if (this.tagtype == 'a') {
4635 cfg.cn[0].cls = 'nav-link';
4638 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4641 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4643 if(this.glyphicon) {
4644 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4649 cfg.cn[0].html += " <span class='caret'></span>";
4653 if (this.badge !== '') {
4655 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4663 initEvents: function()
4665 if (typeof (this.menu) != 'undefined') {
4666 this.menu.parentType = this.xtype;
4667 this.menu.triggerEl = this.el;
4668 this.menu = this.addxtype(Roo.apply({}, this.menu));
4671 this.el.select('a',true).on('click', this.onClick, this);
4673 if(this.tagtype == 'span'){
4674 this.el.select('span',true).on('click', this.onClick, this);
4677 // at this point parent should be available..
4678 this.parent().register(this);
4681 onClick : function(e)
4683 if (e.getTarget('.dropdown-menu-item')) {
4684 // did you click on a menu itemm.... - then don't trigger onclick..
4689 this.preventDefault ||
4692 Roo.log("NavItem - prevent Default?");
4696 if (this.disabled) {
4700 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4701 if (tg && tg.transition) {
4702 Roo.log("waiting for the transitionend");
4708 //Roo.log("fire event clicked");
4709 if(this.fireEvent('click', this, e) === false){
4713 if(this.tagtype == 'span'){
4717 //Roo.log(this.href);
4718 var ael = this.el.select('a',true).first();
4721 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4722 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4723 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4724 return; // ignore... - it's a 'hash' to another page.
4726 Roo.log("NavItem - prevent Default?");
4728 this.scrollToElement(e);
4732 var p = this.parent();
4734 if (['tabs','pills'].indexOf(p.type)!==-1) {
4735 if (typeof(p.setActiveItem) !== 'undefined') {
4736 p.setActiveItem(this);
4740 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4741 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4742 // remove the collapsed menu expand...
4743 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4747 isActive: function () {
4750 setActive : function(state, fire, is_was_active)
4752 if (this.active && !state && this.navId) {
4753 this.was_active = true;
4754 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4756 nv.clearWasActive(this);
4760 this.active = state;
4763 this.el.removeClass('active');
4764 } else if (!this.el.hasClass('active')) {
4765 this.el.addClass('active');
4768 this.fireEvent('changed', this, state);
4771 // show a panel if it's registered and related..
4773 if (!this.navId || !this.tabId || !state || is_was_active) {
4777 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4781 var pan = tg.getPanelByName(this.tabId);
4785 // if we can not flip to new panel - go back to old nav highlight..
4786 if (false == tg.showPanel(pan)) {
4787 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4789 var onav = nv.getWasActive();
4791 onav.setActive(true, false, true);
4800 // this should not be here...
4801 setDisabled : function(state)
4803 this.disabled = state;
4805 this.el.removeClass('disabled');
4806 } else if (!this.el.hasClass('disabled')) {
4807 this.el.addClass('disabled');
4813 * Fetch the element to display the tooltip on.
4814 * @return {Roo.Element} defaults to this.el
4816 tooltipEl : function()
4818 return this.el.select('' + this.tagtype + '', true).first();
4821 scrollToElement : function(e)
4823 var c = document.body;
4826 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4828 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4829 c = document.documentElement;
4832 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4838 var o = target.calcOffsetsTo(c);
4845 this.fireEvent('scrollto', this, options, e);
4847 Roo.get(c).scrollTo('top', options.value, true);
4860 * <span> icon </span>
4861 * <span> text </span>
4862 * <span>badge </span>
4866 * @class Roo.bootstrap.NavSidebarItem
4867 * @extends Roo.bootstrap.NavItem
4868 * Bootstrap Navbar.NavSidebarItem class
4869 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4870 * {Boolean} open is the menu open
4871 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4872 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4873 * {String} buttonSize (sm|md|lg)the extra classes for the button
4874 * {Boolean} showArrow show arrow next to the text (default true)
4876 * Create a new Navbar Button
4877 * @param {Object} config The config object
4879 Roo.bootstrap.NavSidebarItem = function(config){
4880 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4885 * The raw click event for the entire grid.
4886 * @param {Roo.EventObject} e
4891 * Fires when the active item active state changes
4892 * @param {Roo.bootstrap.NavSidebarItem} this
4893 * @param {boolean} state the new state
4901 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4903 badgeWeight : 'default',
4909 buttonWeight : 'default',
4915 getAutoCreate : function(){
4920 href : this.href || '#',
4926 if(this.buttonView){
4929 href : this.href || '#',
4930 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4943 cfg.cls += ' active';
4946 if (this.disabled) {
4947 cfg.cls += ' disabled';
4950 cfg.cls += ' open x-open';
4953 if (this.glyphicon || this.icon) {
4954 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4955 a.cn.push({ tag : 'i', cls : c }) ;
4958 if(!this.buttonView){
4961 html : this.html || ''
4968 if (this.badge !== '') {
4969 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4975 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4978 a.cls += ' dropdown-toggle treeview' ;
4984 initEvents : function()
4986 if (typeof (this.menu) != 'undefined') {
4987 this.menu.parentType = this.xtype;
4988 this.menu.triggerEl = this.el;
4989 this.menu = this.addxtype(Roo.apply({}, this.menu));
4992 this.el.on('click', this.onClick, this);
4994 if(this.badge !== ''){
4995 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5000 onClick : function(e)
5007 if(this.preventDefault){
5011 this.fireEvent('click', this);
5014 disable : function()
5016 this.setDisabled(true);
5021 this.setDisabled(false);
5024 setDisabled : function(state)
5026 if(this.disabled == state){
5030 this.disabled = state;
5033 this.el.addClass('disabled');
5037 this.el.removeClass('disabled');
5042 setActive : function(state)
5044 if(this.active == state){
5048 this.active = state;
5051 this.el.addClass('active');
5055 this.el.removeClass('active');
5060 isActive: function ()
5065 setBadge : function(str)
5071 this.badgeEl.dom.innerHTML = str;
5088 * @class Roo.bootstrap.Row
5089 * @extends Roo.bootstrap.Component
5090 * Bootstrap Row class (contains columns...)
5094 * @param {Object} config The config object
5097 Roo.bootstrap.Row = function(config){
5098 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5101 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5103 getAutoCreate : function(){
5122 * @class Roo.bootstrap.Element
5123 * @extends Roo.bootstrap.Component
5124 * Bootstrap Element class
5125 * @cfg {String} html contents of the element
5126 * @cfg {String} tag tag of the element
5127 * @cfg {String} cls class of the element
5128 * @cfg {Boolean} preventDefault (true|false) default false
5129 * @cfg {Boolean} clickable (true|false) default false
5132 * Create a new Element
5133 * @param {Object} config The config object
5136 Roo.bootstrap.Element = function(config){
5137 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5143 * When a element is chick
5144 * @param {Roo.bootstrap.Element} this
5145 * @param {Roo.EventObject} e
5151 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5156 preventDefault: false,
5159 getAutoCreate : function(){
5163 // cls: this.cls, double assign in parent class Component.js :: onRender
5170 initEvents: function()
5172 Roo.bootstrap.Element.superclass.initEvents.call(this);
5175 this.el.on('click', this.onClick, this);
5180 onClick : function(e)
5182 if(this.preventDefault){
5186 this.fireEvent('click', this, e);
5189 getValue : function()
5191 return this.el.dom.innerHTML;
5194 setValue : function(value)
5196 this.el.dom.innerHTML = value;
5211 * @class Roo.bootstrap.Pagination
5212 * @extends Roo.bootstrap.Component
5213 * Bootstrap Pagination class
5214 * @cfg {String} size xs | sm | md | lg
5215 * @cfg {Boolean} inverse false | true
5218 * Create a new Pagination
5219 * @param {Object} config The config object
5222 Roo.bootstrap.Pagination = function(config){
5223 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5226 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5232 getAutoCreate : function(){
5238 cfg.cls += ' inverse';
5244 cfg.cls += " " + this.cls;
5262 * @class Roo.bootstrap.PaginationItem
5263 * @extends Roo.bootstrap.Component
5264 * Bootstrap PaginationItem class
5265 * @cfg {String} html text
5266 * @cfg {String} href the link
5267 * @cfg {Boolean} preventDefault (true | false) default true
5268 * @cfg {Boolean} active (true | false) default false
5269 * @cfg {Boolean} disabled default false
5273 * Create a new PaginationItem
5274 * @param {Object} config The config object
5278 Roo.bootstrap.PaginationItem = function(config){
5279 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5284 * The raw click event for the entire grid.
5285 * @param {Roo.EventObject} e
5291 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5295 preventDefault: true,
5300 getAutoCreate : function(){
5306 href : this.href ? this.href : '#',
5307 html : this.html ? this.html : ''
5317 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5321 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5327 initEvents: function() {
5329 this.el.on('click', this.onClick, this);
5332 onClick : function(e)
5334 Roo.log('PaginationItem on click ');
5335 if(this.preventDefault){
5343 this.fireEvent('click', this, e);
5359 * @class Roo.bootstrap.Slider
5360 * @extends Roo.bootstrap.Component
5361 * Bootstrap Slider class
5364 * Create a new Slider
5365 * @param {Object} config The config object
5368 Roo.bootstrap.Slider = function(config){
5369 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5372 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5374 getAutoCreate : function(){
5378 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5382 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5394 * Ext JS Library 1.1.1
5395 * Copyright(c) 2006-2007, Ext JS, LLC.
5397 * Originally Released Under LGPL - original licence link has changed is not relivant.
5400 * <script type="text/javascript">
5405 * @class Roo.grid.ColumnModel
5406 * @extends Roo.util.Observable
5407 * This is the default implementation of a ColumnModel used by the Grid. It defines
5408 * the columns in the grid.
5411 var colModel = new Roo.grid.ColumnModel([
5412 {header: "Ticker", width: 60, sortable: true, locked: true},
5413 {header: "Company Name", width: 150, sortable: true},
5414 {header: "Market Cap.", width: 100, sortable: true},
5415 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5416 {header: "Employees", width: 100, sortable: true, resizable: false}
5421 * The config options listed for this class are options which may appear in each
5422 * individual column definition.
5423 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5425 * @param {Object} config An Array of column config objects. See this class's
5426 * config objects for details.
5428 Roo.grid.ColumnModel = function(config){
5430 * The config passed into the constructor
5432 this.config = config;
5435 // if no id, create one
5436 // if the column does not have a dataIndex mapping,
5437 // map it to the order it is in the config
5438 for(var i = 0, len = config.length; i < len; i++){
5440 if(typeof c.dataIndex == "undefined"){
5443 if(typeof c.renderer == "string"){
5444 c.renderer = Roo.util.Format[c.renderer];
5446 if(typeof c.id == "undefined"){
5449 if(c.editor && c.editor.xtype){
5450 c.editor = Roo.factory(c.editor, Roo.grid);
5452 if(c.editor && c.editor.isFormField){
5453 c.editor = new Roo.grid.GridEditor(c.editor);
5455 this.lookup[c.id] = c;
5459 * The width of columns which have no width specified (defaults to 100)
5462 this.defaultWidth = 100;
5465 * Default sortable of columns which have no sortable specified (defaults to false)
5468 this.defaultSortable = false;
5472 * @event widthchange
5473 * Fires when the width of a column changes.
5474 * @param {ColumnModel} this
5475 * @param {Number} columnIndex The column index
5476 * @param {Number} newWidth The new width
5478 "widthchange": true,
5480 * @event headerchange
5481 * Fires when the text of a header changes.
5482 * @param {ColumnModel} this
5483 * @param {Number} columnIndex The column index
5484 * @param {Number} newText The new header text
5486 "headerchange": true,
5488 * @event hiddenchange
5489 * Fires when a column is hidden or "unhidden".
5490 * @param {ColumnModel} this
5491 * @param {Number} columnIndex The column index
5492 * @param {Boolean} hidden true if hidden, false otherwise
5494 "hiddenchange": true,
5496 * @event columnmoved
5497 * Fires when a column is moved.
5498 * @param {ColumnModel} this
5499 * @param {Number} oldIndex
5500 * @param {Number} newIndex
5502 "columnmoved" : true,
5504 * @event columlockchange
5505 * Fires when a column's locked state is changed
5506 * @param {ColumnModel} this
5507 * @param {Number} colIndex
5508 * @param {Boolean} locked true if locked
5510 "columnlockchange" : true
5512 Roo.grid.ColumnModel.superclass.constructor.call(this);
5514 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5516 * @cfg {String} header The header text to display in the Grid view.
5519 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5520 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5521 * specified, the column's index is used as an index into the Record's data Array.
5524 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5525 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5528 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5529 * Defaults to the value of the {@link #defaultSortable} property.
5530 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5533 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5536 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5539 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5542 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5545 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5546 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5547 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5548 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5551 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5554 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5557 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5560 * @cfg {String} cursor (Optional)
5563 * @cfg {String} tooltip (Optional)
5566 * @cfg {Number} xs (Optional)
5569 * @cfg {Number} sm (Optional)
5572 * @cfg {Number} md (Optional)
5575 * @cfg {Number} lg (Optional)
5578 * Returns the id of the column at the specified index.
5579 * @param {Number} index The column index
5580 * @return {String} the id
5582 getColumnId : function(index){
5583 return this.config[index].id;
5587 * Returns the column for a specified id.
5588 * @param {String} id The column id
5589 * @return {Object} the column
5591 getColumnById : function(id){
5592 return this.lookup[id];
5597 * Returns the column for a specified dataIndex.
5598 * @param {String} dataIndex The column dataIndex
5599 * @return {Object|Boolean} the column or false if not found
5601 getColumnByDataIndex: function(dataIndex){
5602 var index = this.findColumnIndex(dataIndex);
5603 return index > -1 ? this.config[index] : false;
5607 * Returns the index for a specified column id.
5608 * @param {String} id The column id
5609 * @return {Number} the index, or -1 if not found
5611 getIndexById : function(id){
5612 for(var i = 0, len = this.config.length; i < len; i++){
5613 if(this.config[i].id == id){
5621 * Returns the index for a specified column dataIndex.
5622 * @param {String} dataIndex The column dataIndex
5623 * @return {Number} the index, or -1 if not found
5626 findColumnIndex : function(dataIndex){
5627 for(var i = 0, len = this.config.length; i < len; i++){
5628 if(this.config[i].dataIndex == dataIndex){
5636 moveColumn : function(oldIndex, newIndex){
5637 var c = this.config[oldIndex];
5638 this.config.splice(oldIndex, 1);
5639 this.config.splice(newIndex, 0, c);
5640 this.dataMap = null;
5641 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5644 isLocked : function(colIndex){
5645 return this.config[colIndex].locked === true;
5648 setLocked : function(colIndex, value, suppressEvent){
5649 if(this.isLocked(colIndex) == value){
5652 this.config[colIndex].locked = value;
5654 this.fireEvent("columnlockchange", this, colIndex, value);
5658 getTotalLockedWidth : function(){
5660 for(var i = 0; i < this.config.length; i++){
5661 if(this.isLocked(i) && !this.isHidden(i)){
5662 this.totalWidth += this.getColumnWidth(i);
5668 getLockedCount : function(){
5669 for(var i = 0, len = this.config.length; i < len; i++){
5670 if(!this.isLocked(i)){
5675 return this.config.length;
5679 * Returns the number of columns.
5682 getColumnCount : function(visibleOnly){
5683 if(visibleOnly === true){
5685 for(var i = 0, len = this.config.length; i < len; i++){
5686 if(!this.isHidden(i)){
5692 return this.config.length;
5696 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5697 * @param {Function} fn
5698 * @param {Object} scope (optional)
5699 * @return {Array} result
5701 getColumnsBy : function(fn, scope){
5703 for(var i = 0, len = this.config.length; i < len; i++){
5704 var c = this.config[i];
5705 if(fn.call(scope||this, c, i) === true){
5713 * Returns true if the specified column is sortable.
5714 * @param {Number} col The column index
5717 isSortable : function(col){
5718 if(typeof this.config[col].sortable == "undefined"){
5719 return this.defaultSortable;
5721 return this.config[col].sortable;
5725 * Returns the rendering (formatting) function defined for the column.
5726 * @param {Number} col The column index.
5727 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5729 getRenderer : function(col){
5730 if(!this.config[col].renderer){
5731 return Roo.grid.ColumnModel.defaultRenderer;
5733 return this.config[col].renderer;
5737 * Sets the rendering (formatting) function for a column.
5738 * @param {Number} col The column index
5739 * @param {Function} fn The function to use to process the cell's raw data
5740 * to return HTML markup for the grid view. The render function is called with
5741 * the following parameters:<ul>
5742 * <li>Data value.</li>
5743 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5744 * <li>css A CSS style string to apply to the table cell.</li>
5745 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5746 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5747 * <li>Row index</li>
5748 * <li>Column index</li>
5749 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5751 setRenderer : function(col, fn){
5752 this.config[col].renderer = fn;
5756 * Returns the width for the specified column.
5757 * @param {Number} col The column index
5760 getColumnWidth : function(col){
5761 return this.config[col].width * 1 || this.defaultWidth;
5765 * Sets the width for a column.
5766 * @param {Number} col The column index
5767 * @param {Number} width The new width
5769 setColumnWidth : function(col, width, suppressEvent){
5770 this.config[col].width = width;
5771 this.totalWidth = null;
5773 this.fireEvent("widthchange", this, col, width);
5778 * Returns the total width of all columns.
5779 * @param {Boolean} includeHidden True to include hidden column widths
5782 getTotalWidth : function(includeHidden){
5783 if(!this.totalWidth){
5784 this.totalWidth = 0;
5785 for(var i = 0, len = this.config.length; i < len; i++){
5786 if(includeHidden || !this.isHidden(i)){
5787 this.totalWidth += this.getColumnWidth(i);
5791 return this.totalWidth;
5795 * Returns the header for the specified column.
5796 * @param {Number} col The column index
5799 getColumnHeader : function(col){
5800 return this.config[col].header;
5804 * Sets the header for a column.
5805 * @param {Number} col The column index
5806 * @param {String} header The new header
5808 setColumnHeader : function(col, header){
5809 this.config[col].header = header;
5810 this.fireEvent("headerchange", this, col, header);
5814 * Returns the tooltip for the specified column.
5815 * @param {Number} col The column index
5818 getColumnTooltip : function(col){
5819 return this.config[col].tooltip;
5822 * Sets the tooltip for a column.
5823 * @param {Number} col The column index
5824 * @param {String} tooltip The new tooltip
5826 setColumnTooltip : function(col, tooltip){
5827 this.config[col].tooltip = tooltip;
5831 * Returns the dataIndex for the specified column.
5832 * @param {Number} col The column index
5835 getDataIndex : function(col){
5836 return this.config[col].dataIndex;
5840 * Sets the dataIndex for a column.
5841 * @param {Number} col The column index
5842 * @param {Number} dataIndex The new dataIndex
5844 setDataIndex : function(col, dataIndex){
5845 this.config[col].dataIndex = dataIndex;
5851 * Returns true if the cell is editable.
5852 * @param {Number} colIndex The column index
5853 * @param {Number} rowIndex The row index - this is nto actually used..?
5856 isCellEditable : function(colIndex, rowIndex){
5857 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5861 * Returns the editor defined for the cell/column.
5862 * return false or null to disable editing.
5863 * @param {Number} colIndex The column index
5864 * @param {Number} rowIndex The row index
5867 getCellEditor : function(colIndex, rowIndex){
5868 return this.config[colIndex].editor;
5872 * Sets if a column is editable.
5873 * @param {Number} col The column index
5874 * @param {Boolean} editable True if the column is editable
5876 setEditable : function(col, editable){
5877 this.config[col].editable = editable;
5882 * Returns true if the column is hidden.
5883 * @param {Number} colIndex The column index
5886 isHidden : function(colIndex){
5887 return this.config[colIndex].hidden;
5892 * Returns true if the column width cannot be changed
5894 isFixed : function(colIndex){
5895 return this.config[colIndex].fixed;
5899 * Returns true if the column can be resized
5902 isResizable : function(colIndex){
5903 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5906 * Sets if a column is hidden.
5907 * @param {Number} colIndex The column index
5908 * @param {Boolean} hidden True if the column is hidden
5910 setHidden : function(colIndex, hidden){
5911 this.config[colIndex].hidden = hidden;
5912 this.totalWidth = null;
5913 this.fireEvent("hiddenchange", this, colIndex, hidden);
5917 * Sets the editor for a column.
5918 * @param {Number} col The column index
5919 * @param {Object} editor The editor object
5921 setEditor : function(col, editor){
5922 this.config[col].editor = editor;
5926 Roo.grid.ColumnModel.defaultRenderer = function(value)
5928 if(typeof value == "object") {
5931 if(typeof value == "string" && value.length < 1){
5935 return String.format("{0}", value);
5938 // Alias for backwards compatibility
5939 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5942 * Ext JS Library 1.1.1
5943 * Copyright(c) 2006-2007, Ext JS, LLC.
5945 * Originally Released Under LGPL - original licence link has changed is not relivant.
5948 * <script type="text/javascript">
5952 * @class Roo.LoadMask
5953 * A simple utility class for generically masking elements while loading data. If the element being masked has
5954 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5955 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5956 * element's UpdateManager load indicator and will be destroyed after the initial load.
5958 * Create a new LoadMask
5959 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5960 * @param {Object} config The config object
5962 Roo.LoadMask = function(el, config){
5963 this.el = Roo.get(el);
5964 Roo.apply(this, config);
5966 this.store.on('beforeload', this.onBeforeLoad, this);
5967 this.store.on('load', this.onLoad, this);
5968 this.store.on('loadexception', this.onLoadException, this);
5969 this.removeMask = false;
5971 var um = this.el.getUpdateManager();
5972 um.showLoadIndicator = false; // disable the default indicator
5973 um.on('beforeupdate', this.onBeforeLoad, this);
5974 um.on('update', this.onLoad, this);
5975 um.on('failure', this.onLoad, this);
5976 this.removeMask = true;
5980 Roo.LoadMask.prototype = {
5982 * @cfg {Boolean} removeMask
5983 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5984 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5988 * The text to display in a centered loading message box (defaults to 'Loading...')
5992 * @cfg {String} msgCls
5993 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5995 msgCls : 'x-mask-loading',
5998 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6004 * Disables the mask to prevent it from being displayed
6006 disable : function(){
6007 this.disabled = true;
6011 * Enables the mask so that it can be displayed
6013 enable : function(){
6014 this.disabled = false;
6017 onLoadException : function()
6021 if (typeof(arguments[3]) != 'undefined') {
6022 Roo.MessageBox.alert("Error loading",arguments[3]);
6026 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6027 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6034 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6039 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6043 onBeforeLoad : function(){
6045 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6050 destroy : function(){
6052 this.store.un('beforeload', this.onBeforeLoad, this);
6053 this.store.un('load', this.onLoad, this);
6054 this.store.un('loadexception', this.onLoadException, this);
6056 var um = this.el.getUpdateManager();
6057 um.un('beforeupdate', this.onBeforeLoad, this);
6058 um.un('update', this.onLoad, this);
6059 um.un('failure', this.onLoad, this);
6070 * @class Roo.bootstrap.Table
6071 * @extends Roo.bootstrap.Component
6072 * Bootstrap Table class
6073 * @cfg {String} cls table class
6074 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6075 * @cfg {String} bgcolor Specifies the background color for a table
6076 * @cfg {Number} border Specifies whether the table cells should have borders or not
6077 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6078 * @cfg {Number} cellspacing Specifies the space between cells
6079 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6080 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6081 * @cfg {String} sortable Specifies that the table should be sortable
6082 * @cfg {String} summary Specifies a summary of the content of a table
6083 * @cfg {Number} width Specifies the width of a table
6084 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6086 * @cfg {boolean} striped Should the rows be alternative striped
6087 * @cfg {boolean} bordered Add borders to the table
6088 * @cfg {boolean} hover Add hover highlighting
6089 * @cfg {boolean} condensed Format condensed
6090 * @cfg {boolean} responsive Format condensed
6091 * @cfg {Boolean} loadMask (true|false) default false
6092 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6093 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6094 * @cfg {Boolean} rowSelection (true|false) default false
6095 * @cfg {Boolean} cellSelection (true|false) default false
6096 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6097 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6098 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6099 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6103 * Create a new Table
6104 * @param {Object} config The config object
6107 Roo.bootstrap.Table = function(config){
6108 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6113 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6114 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6115 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6116 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6118 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6120 this.sm.grid = this;
6121 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6122 this.sm = this.selModel;
6123 this.sm.xmodule = this.xmodule || false;
6126 if (this.cm && typeof(this.cm.config) == 'undefined') {
6127 this.colModel = new Roo.grid.ColumnModel(this.cm);
6128 this.cm = this.colModel;
6129 this.cm.xmodule = this.xmodule || false;
6132 this.store= Roo.factory(this.store, Roo.data);
6133 this.ds = this.store;
6134 this.ds.xmodule = this.xmodule || false;
6137 if (this.footer && this.store) {
6138 this.footer.dataSource = this.ds;
6139 this.footer = Roo.factory(this.footer);
6146 * Fires when a cell is clicked
6147 * @param {Roo.bootstrap.Table} this
6148 * @param {Roo.Element} el
6149 * @param {Number} rowIndex
6150 * @param {Number} columnIndex
6151 * @param {Roo.EventObject} e
6155 * @event celldblclick
6156 * Fires when a cell is double clicked
6157 * @param {Roo.bootstrap.Table} this
6158 * @param {Roo.Element} el
6159 * @param {Number} rowIndex
6160 * @param {Number} columnIndex
6161 * @param {Roo.EventObject} e
6163 "celldblclick" : true,
6166 * Fires when a row is clicked
6167 * @param {Roo.bootstrap.Table} this
6168 * @param {Roo.Element} el
6169 * @param {Number} rowIndex
6170 * @param {Roo.EventObject} e
6174 * @event rowdblclick
6175 * Fires when a row is double clicked
6176 * @param {Roo.bootstrap.Table} this
6177 * @param {Roo.Element} el
6178 * @param {Number} rowIndex
6179 * @param {Roo.EventObject} e
6181 "rowdblclick" : true,
6184 * Fires when a mouseover occur
6185 * @param {Roo.bootstrap.Table} this
6186 * @param {Roo.Element} el
6187 * @param {Number} rowIndex
6188 * @param {Number} columnIndex
6189 * @param {Roo.EventObject} e
6194 * Fires when a mouseout occur
6195 * @param {Roo.bootstrap.Table} this
6196 * @param {Roo.Element} el
6197 * @param {Number} rowIndex
6198 * @param {Number} columnIndex
6199 * @param {Roo.EventObject} e
6204 * Fires when a row is rendered, so you can change add a style to it.
6205 * @param {Roo.bootstrap.Table} this
6206 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6210 * @event rowsrendered
6211 * Fires when all the rows have been rendered
6212 * @param {Roo.bootstrap.Table} this
6214 'rowsrendered' : true,
6216 * @event contextmenu
6217 * The raw contextmenu event for the entire grid.
6218 * @param {Roo.EventObject} e
6220 "contextmenu" : true,
6222 * @event rowcontextmenu
6223 * Fires when a row is right clicked
6224 * @param {Roo.bootstrap.Table} this
6225 * @param {Number} rowIndex
6226 * @param {Roo.EventObject} e
6228 "rowcontextmenu" : true,
6230 * @event cellcontextmenu
6231 * Fires when a cell is right clicked
6232 * @param {Roo.bootstrap.Table} this
6233 * @param {Number} rowIndex
6234 * @param {Number} cellIndex
6235 * @param {Roo.EventObject} e
6237 "cellcontextmenu" : true,
6239 * @event headercontextmenu
6240 * Fires when a header is right clicked
6241 * @param {Roo.bootstrap.Table} this
6242 * @param {Number} columnIndex
6243 * @param {Roo.EventObject} e
6245 "headercontextmenu" : true
6249 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6275 rowSelection : false,
6276 cellSelection : false,
6279 // Roo.Element - the tbody
6281 // Roo.Element - thead element
6284 container: false, // used by gridpanel...
6290 auto_hide_footer : false,
6292 getAutoCreate : function()
6294 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6301 if (this.scrollBody) {
6302 cfg.cls += ' table-body-fixed';
6305 cfg.cls += ' table-striped';
6309 cfg.cls += ' table-hover';
6311 if (this.bordered) {
6312 cfg.cls += ' table-bordered';
6314 if (this.condensed) {
6315 cfg.cls += ' table-condensed';
6317 if (this.responsive) {
6318 cfg.cls += ' table-responsive';
6322 cfg.cls+= ' ' +this.cls;
6325 // this lot should be simplifed...
6338 ].forEach(function(k) {
6346 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6349 if(this.store || this.cm){
6350 if(this.headerShow){
6351 cfg.cn.push(this.renderHeader());
6354 cfg.cn.push(this.renderBody());
6356 if(this.footerShow){
6357 cfg.cn.push(this.renderFooter());
6359 // where does this come from?
6360 //cfg.cls+= ' TableGrid';
6363 return { cn : [ cfg ] };
6366 initEvents : function()
6368 if(!this.store || !this.cm){
6371 if (this.selModel) {
6372 this.selModel.initEvents();
6376 //Roo.log('initEvents with ds!!!!');
6378 this.mainBody = this.el.select('tbody', true).first();
6379 this.mainHead = this.el.select('thead', true).first();
6380 this.mainFoot = this.el.select('tfoot', true).first();
6386 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6387 e.on('click', _this.sort, _this);
6390 this.mainBody.on("click", this.onClick, this);
6391 this.mainBody.on("dblclick", this.onDblClick, this);
6393 // why is this done????? = it breaks dialogs??
6394 //this.parent().el.setStyle('position', 'relative');
6398 this.footer.parentId = this.id;
6399 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6402 this.el.select('tfoot tr td').first().addClass('hide');
6407 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6410 this.store.on('load', this.onLoad, this);
6411 this.store.on('beforeload', this.onBeforeLoad, this);
6412 this.store.on('update', this.onUpdate, this);
6413 this.store.on('add', this.onAdd, this);
6414 this.store.on("clear", this.clear, this);
6416 this.el.on("contextmenu", this.onContextMenu, this);
6418 this.mainBody.on('scroll', this.onBodyScroll, this);
6420 this.cm.on("headerchange", this.onHeaderChange, this);
6422 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6426 onContextMenu : function(e, t)
6428 this.processEvent("contextmenu", e);
6431 processEvent : function(name, e)
6433 if (name != 'touchstart' ) {
6434 this.fireEvent(name, e);
6437 var t = e.getTarget();
6439 var cell = Roo.get(t);
6445 if(cell.findParent('tfoot', false, true)){
6449 if(cell.findParent('thead', false, true)){
6451 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6452 cell = Roo.get(t).findParent('th', false, true);
6454 Roo.log("failed to find th in thead?");
6455 Roo.log(e.getTarget());
6460 var cellIndex = cell.dom.cellIndex;
6462 var ename = name == 'touchstart' ? 'click' : name;
6463 this.fireEvent("header" + ename, this, cellIndex, e);
6468 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6469 cell = Roo.get(t).findParent('td', false, true);
6471 Roo.log("failed to find th in tbody?");
6472 Roo.log(e.getTarget());
6477 var row = cell.findParent('tr', false, true);
6478 var cellIndex = cell.dom.cellIndex;
6479 var rowIndex = row.dom.rowIndex - 1;
6483 this.fireEvent("row" + name, this, rowIndex, e);
6487 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6493 onMouseover : function(e, el)
6495 var cell = Roo.get(el);
6501 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6502 cell = cell.findParent('td', false, true);
6505 var row = cell.findParent('tr', false, true);
6506 var cellIndex = cell.dom.cellIndex;
6507 var rowIndex = row.dom.rowIndex - 1; // start from 0
6509 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6513 onMouseout : function(e, el)
6515 var cell = Roo.get(el);
6521 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6522 cell = cell.findParent('td', false, true);
6525 var row = cell.findParent('tr', false, true);
6526 var cellIndex = cell.dom.cellIndex;
6527 var rowIndex = row.dom.rowIndex - 1; // start from 0
6529 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6533 onClick : function(e, el)
6535 var cell = Roo.get(el);
6537 if(!cell || (!this.cellSelection && !this.rowSelection)){
6541 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6542 cell = cell.findParent('td', false, true);
6545 if(!cell || typeof(cell) == 'undefined'){
6549 var row = cell.findParent('tr', false, true);
6551 if(!row || typeof(row) == 'undefined'){
6555 var cellIndex = cell.dom.cellIndex;
6556 var rowIndex = this.getRowIndex(row);
6558 // why??? - should these not be based on SelectionModel?
6559 if(this.cellSelection){
6560 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6563 if(this.rowSelection){
6564 this.fireEvent('rowclick', this, row, rowIndex, e);
6570 onDblClick : function(e,el)
6572 var cell = Roo.get(el);
6574 if(!cell || (!this.cellSelection && !this.rowSelection)){
6578 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6579 cell = cell.findParent('td', false, true);
6582 if(!cell || typeof(cell) == 'undefined'){
6586 var row = cell.findParent('tr', false, true);
6588 if(!row || typeof(row) == 'undefined'){
6592 var cellIndex = cell.dom.cellIndex;
6593 var rowIndex = this.getRowIndex(row);
6595 if(this.cellSelection){
6596 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6599 if(this.rowSelection){
6600 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6604 sort : function(e,el)
6606 var col = Roo.get(el);
6608 if(!col.hasClass('sortable')){
6612 var sort = col.attr('sort');
6615 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6619 this.store.sortInfo = {field : sort, direction : dir};
6622 Roo.log("calling footer first");
6623 this.footer.onClick('first');
6626 this.store.load({ params : { start : 0 } });
6630 renderHeader : function()
6638 this.totalWidth = 0;
6640 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642 var config = cm.config[i];
6646 cls : 'x-hcol-' + i,
6648 html: cm.getColumnHeader(i)
6653 if(typeof(config.sortable) != 'undefined' && config.sortable){
6655 c.html = '<i class="glyphicon"></i>' + c.html;
6658 if(typeof(config.lgHeader) != 'undefined'){
6659 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6662 if(typeof(config.mdHeader) != 'undefined'){
6663 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6666 if(typeof(config.smHeader) != 'undefined'){
6667 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6670 if(typeof(config.xsHeader) != 'undefined'){
6671 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6678 if(typeof(config.tooltip) != 'undefined'){
6679 c.tooltip = config.tooltip;
6682 if(typeof(config.colspan) != 'undefined'){
6683 c.colspan = config.colspan;
6686 if(typeof(config.hidden) != 'undefined' && config.hidden){
6687 c.style += ' display:none;';
6690 if(typeof(config.dataIndex) != 'undefined'){
6691 c.sort = config.dataIndex;
6696 if(typeof(config.align) != 'undefined' && config.align.length){
6697 c.style += ' text-align:' + config.align + ';';
6700 if(typeof(config.width) != 'undefined'){
6701 c.style += ' width:' + config.width + 'px;';
6702 this.totalWidth += config.width;
6704 this.totalWidth += 100; // assume minimum of 100 per column?
6707 if(typeof(config.cls) != 'undefined'){
6708 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6711 ['xs','sm','md','lg'].map(function(size){
6713 if(typeof(config[size]) == 'undefined'){
6717 if (!config[size]) { // 0 = hidden
6718 c.cls += ' hidden-' + size;
6722 c.cls += ' col-' + size + '-' + config[size];
6732 renderBody : function()
6742 colspan : this.cm.getColumnCount()
6752 renderFooter : function()
6762 colspan : this.cm.getColumnCount()
6776 // Roo.log('ds onload');
6781 var ds = this.store;
6783 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6784 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6785 if (_this.store.sortInfo) {
6787 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6788 e.select('i', true).addClass(['glyphicon-arrow-up']);
6791 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6792 e.select('i', true).addClass(['glyphicon-arrow-down']);
6797 var tbody = this.mainBody;
6799 if(ds.getCount() > 0){
6800 ds.data.each(function(d,rowIndex){
6801 var row = this.renderRow(cm, ds, rowIndex);
6803 tbody.createChild(row);
6807 if(row.cellObjects.length){
6808 Roo.each(row.cellObjects, function(r){
6809 _this.renderCellObject(r);
6816 var tfoot = this.el.select('tfoot', true).first();
6818 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6820 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6822 var total = this.ds.getTotalCount();
6824 if(this.footer.pageSize < total){
6825 this.mainFoot.show();
6829 Roo.each(this.el.select('tbody td', true).elements, function(e){
6830 e.on('mouseover', _this.onMouseover, _this);
6833 Roo.each(this.el.select('tbody td', true).elements, function(e){
6834 e.on('mouseout', _this.onMouseout, _this);
6836 this.fireEvent('rowsrendered', this);
6842 onUpdate : function(ds,record)
6844 this.refreshRow(record);
6848 onRemove : function(ds, record, index, isUpdate){
6849 if(isUpdate !== true){
6850 this.fireEvent("beforerowremoved", this, index, record);
6852 var bt = this.mainBody.dom;
6854 var rows = this.el.select('tbody > tr', true).elements;
6856 if(typeof(rows[index]) != 'undefined'){
6857 bt.removeChild(rows[index].dom);
6860 // if(bt.rows[index]){
6861 // bt.removeChild(bt.rows[index]);
6864 if(isUpdate !== true){
6865 //this.stripeRows(index);
6866 //this.syncRowHeights(index, index);
6868 this.fireEvent("rowremoved", this, index, record);
6872 onAdd : function(ds, records, rowIndex)
6874 //Roo.log('on Add called');
6875 // - note this does not handle multiple adding very well..
6876 var bt = this.mainBody.dom;
6877 for (var i =0 ; i < records.length;i++) {
6878 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6879 //Roo.log(records[i]);
6880 //Roo.log(this.store.getAt(rowIndex+i));
6881 this.insertRow(this.store, rowIndex + i, false);
6888 refreshRow : function(record){
6889 var ds = this.store, index;
6890 if(typeof record == 'number'){
6892 record = ds.getAt(index);
6894 index = ds.indexOf(record);
6896 this.insertRow(ds, index, true);
6898 this.onRemove(ds, record, index+1, true);
6900 //this.syncRowHeights(index, index);
6902 this.fireEvent("rowupdated", this, index, record);
6905 insertRow : function(dm, rowIndex, isUpdate){
6908 this.fireEvent("beforerowsinserted", this, rowIndex);
6910 //var s = this.getScrollState();
6911 var row = this.renderRow(this.cm, this.store, rowIndex);
6912 // insert before rowIndex..
6913 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6917 if(row.cellObjects.length){
6918 Roo.each(row.cellObjects, function(r){
6919 _this.renderCellObject(r);
6924 this.fireEvent("rowsinserted", this, rowIndex);
6925 //this.syncRowHeights(firstRow, lastRow);
6926 //this.stripeRows(firstRow);
6933 getRowDom : function(rowIndex)
6935 var rows = this.el.select('tbody > tr', true).elements;
6937 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6940 // returns the object tree for a tr..
6943 renderRow : function(cm, ds, rowIndex)
6945 var d = ds.getAt(rowIndex);
6949 cls : 'x-row-' + rowIndex,
6953 var cellObjects = [];
6955 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6956 var config = cm.config[i];
6958 var renderer = cm.getRenderer(i);
6962 if(typeof(renderer) !== 'undefined'){
6963 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6965 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6966 // and are rendered into the cells after the row is rendered - using the id for the element.
6968 if(typeof(value) === 'object'){
6978 rowIndex : rowIndex,
6983 this.fireEvent('rowclass', this, rowcfg);
6987 cls : rowcfg.rowClass + ' x-col-' + i,
6989 html: (typeof(value) === 'object') ? '' : value
6996 if(typeof(config.colspan) != 'undefined'){
6997 td.colspan = config.colspan;
7000 if(typeof(config.hidden) != 'undefined' && config.hidden){
7001 td.style += ' display:none;';
7004 if(typeof(config.align) != 'undefined' && config.align.length){
7005 td.style += ' text-align:' + config.align + ';';
7007 if(typeof(config.valign) != 'undefined' && config.valign.length){
7008 td.style += ' vertical-align:' + config.valign + ';';
7011 if(typeof(config.width) != 'undefined'){
7012 td.style += ' width:' + config.width + 'px;';
7015 if(typeof(config.cursor) != 'undefined'){
7016 td.style += ' cursor:' + config.cursor + ';';
7019 if(typeof(config.cls) != 'undefined'){
7020 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7023 ['xs','sm','md','lg'].map(function(size){
7025 if(typeof(config[size]) == 'undefined'){
7029 if (!config[size]) { // 0 = hidden
7030 td.cls += ' hidden-' + size;
7034 td.cls += ' col-' + size + '-' + config[size];
7042 row.cellObjects = cellObjects;
7050 onBeforeLoad : function()
7059 this.el.select('tbody', true).first().dom.innerHTML = '';
7062 * Show or hide a row.
7063 * @param {Number} rowIndex to show or hide
7064 * @param {Boolean} state hide
7066 setRowVisibility : function(rowIndex, state)
7068 var bt = this.mainBody.dom;
7070 var rows = this.el.select('tbody > tr', true).elements;
7072 if(typeof(rows[rowIndex]) == 'undefined'){
7075 rows[rowIndex].dom.style.display = state ? '' : 'none';
7079 getSelectionModel : function(){
7081 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7083 return this.selModel;
7086 * Render the Roo.bootstrap object from renderder
7088 renderCellObject : function(r)
7092 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7094 var t = r.cfg.render(r.container);
7097 Roo.each(r.cfg.cn, function(c){
7099 container: t.getChildContainer(),
7102 _this.renderCellObject(child);
7107 getRowIndex : function(row)
7111 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7122 * Returns the grid's underlying element = used by panel.Grid
7123 * @return {Element} The element
7125 getGridEl : function(){
7129 * Forces a resize - used by panel.Grid
7130 * @return {Element} The element
7132 autoSize : function()
7134 //var ctr = Roo.get(this.container.dom.parentElement);
7135 var ctr = Roo.get(this.el.dom);
7137 var thd = this.getGridEl().select('thead',true).first();
7138 var tbd = this.getGridEl().select('tbody', true).first();
7139 var tfd = this.getGridEl().select('tfoot', true).first();
7141 var cw = ctr.getWidth();
7145 tbd.setSize(ctr.getWidth(),
7146 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7148 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7151 cw = Math.max(cw, this.totalWidth);
7152 this.getGridEl().select('tr',true).setWidth(cw);
7153 // resize 'expandable coloumn?
7155 return; // we doe not have a view in this design..
7158 onBodyScroll: function()
7160 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7162 this.mainHead.setStyle({
7163 'position' : 'relative',
7164 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7170 var scrollHeight = this.mainBody.dom.scrollHeight;
7172 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7174 var height = this.mainBody.getHeight();
7176 if(scrollHeight - height == scrollTop) {
7178 var total = this.ds.getTotalCount();
7180 if(this.footer.cursor + this.footer.pageSize < total){
7182 this.footer.ds.load({
7184 start : this.footer.cursor + this.footer.pageSize,
7185 limit : this.footer.pageSize
7195 onHeaderChange : function()
7197 var header = this.renderHeader();
7198 var table = this.el.select('table', true).first();
7200 this.mainHead.remove();
7201 this.mainHead = table.createChild(header, this.mainBody, false);
7204 onHiddenChange : function(colModel, colIndex, hidden)
7206 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7207 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7209 this.CSS.updateRule(thSelector, "display", "");
7210 this.CSS.updateRule(tdSelector, "display", "");
7213 this.CSS.updateRule(thSelector, "display", "none");
7214 this.CSS.updateRule(tdSelector, "display", "none");
7217 this.onHeaderChange();
7221 setColumnWidth: function(col_index, width)
7223 // width = "md-2 xs-2..."
7224 if(!this.colModel.config[col_index]) {
7228 var w = width.split(" ");
7230 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7232 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7235 for(var j = 0; j < w.length; j++) {
7241 var size_cls = w[j].split("-");
7243 if(!Number.isInteger(size_cls[1] * 1)) {
7247 if(!this.colModel.config[col_index][size_cls[0]]) {
7251 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7255 h_row[0].classList.replace(
7256 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7257 "col-"+size_cls[0]+"-"+size_cls[1]
7260 for(var i = 0; i < rows.length; i++) {
7262 var size_cls = w[j].split("-");
7264 if(!Number.isInteger(size_cls[1] * 1)) {
7268 if(!this.colModel.config[col_index][size_cls[0]]) {
7272 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7276 rows[i].classList.replace(
7277 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7278 "col-"+size_cls[0]+"-"+size_cls[1]
7282 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7297 * @class Roo.bootstrap.TableCell
7298 * @extends Roo.bootstrap.Component
7299 * Bootstrap TableCell class
7300 * @cfg {String} html cell contain text
7301 * @cfg {String} cls cell class
7302 * @cfg {String} tag cell tag (td|th) default td
7303 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7304 * @cfg {String} align Aligns the content in a cell
7305 * @cfg {String} axis Categorizes cells
7306 * @cfg {String} bgcolor Specifies the background color of a cell
7307 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7308 * @cfg {Number} colspan Specifies the number of columns a cell should span
7309 * @cfg {String} headers Specifies one or more header cells a cell is related to
7310 * @cfg {Number} height Sets the height of a cell
7311 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7312 * @cfg {Number} rowspan Sets the number of rows a cell should span
7313 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7314 * @cfg {String} valign Vertical aligns the content in a cell
7315 * @cfg {Number} width Specifies the width of a cell
7318 * Create a new TableCell
7319 * @param {Object} config The config object
7322 Roo.bootstrap.TableCell = function(config){
7323 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7326 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7346 getAutoCreate : function(){
7347 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7367 cfg.align=this.align
7373 cfg.bgcolor=this.bgcolor
7376 cfg.charoff=this.charoff
7379 cfg.colspan=this.colspan
7382 cfg.headers=this.headers
7385 cfg.height=this.height
7388 cfg.nowrap=this.nowrap
7391 cfg.rowspan=this.rowspan
7394 cfg.scope=this.scope
7397 cfg.valign=this.valign
7400 cfg.width=this.width
7419 * @class Roo.bootstrap.TableRow
7420 * @extends Roo.bootstrap.Component
7421 * Bootstrap TableRow class
7422 * @cfg {String} cls row class
7423 * @cfg {String} align Aligns the content in a table row
7424 * @cfg {String} bgcolor Specifies a background color for a table row
7425 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7426 * @cfg {String} valign Vertical aligns the content in a table row
7429 * Create a new TableRow
7430 * @param {Object} config The config object
7433 Roo.bootstrap.TableRow = function(config){
7434 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7437 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7445 getAutoCreate : function(){
7446 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7456 cfg.align = this.align;
7459 cfg.bgcolor = this.bgcolor;
7462 cfg.charoff = this.charoff;
7465 cfg.valign = this.valign;
7483 * @class Roo.bootstrap.TableBody
7484 * @extends Roo.bootstrap.Component
7485 * Bootstrap TableBody class
7486 * @cfg {String} cls element class
7487 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7488 * @cfg {String} align Aligns the content inside the element
7489 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7490 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7493 * Create a new TableBody
7494 * @param {Object} config The config object
7497 Roo.bootstrap.TableBody = function(config){
7498 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7501 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7509 getAutoCreate : function(){
7510 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7524 cfg.align = this.align;
7527 cfg.charoff = this.charoff;
7530 cfg.valign = this.valign;
7537 // initEvents : function()
7544 // this.store = Roo.factory(this.store, Roo.data);
7545 // this.store.on('load', this.onLoad, this);
7547 // this.store.load();
7551 // onLoad: function ()
7553 // this.fireEvent('load', this);
7563 * Ext JS Library 1.1.1
7564 * Copyright(c) 2006-2007, Ext JS, LLC.
7566 * Originally Released Under LGPL - original licence link has changed is not relivant.
7569 * <script type="text/javascript">
7572 // as we use this in bootstrap.
7573 Roo.namespace('Roo.form');
7575 * @class Roo.form.Action
7576 * Internal Class used to handle form actions
7578 * @param {Roo.form.BasicForm} el The form element or its id
7579 * @param {Object} config Configuration options
7584 // define the action interface
7585 Roo.form.Action = function(form, options){
7587 this.options = options || {};
7590 * Client Validation Failed
7593 Roo.form.Action.CLIENT_INVALID = 'client';
7595 * Server Validation Failed
7598 Roo.form.Action.SERVER_INVALID = 'server';
7600 * Connect to Server Failed
7603 Roo.form.Action.CONNECT_FAILURE = 'connect';
7605 * Reading Data from Server Failed
7608 Roo.form.Action.LOAD_FAILURE = 'load';
7610 Roo.form.Action.prototype = {
7612 failureType : undefined,
7613 response : undefined,
7617 run : function(options){
7622 success : function(response){
7627 handleResponse : function(response){
7631 // default connection failure
7632 failure : function(response){
7634 this.response = response;
7635 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7636 this.form.afterAction(this, false);
7639 processResponse : function(response){
7640 this.response = response;
7641 if(!response.responseText){
7644 this.result = this.handleResponse(response);
7648 // utility functions used internally
7649 getUrl : function(appendParams){
7650 var url = this.options.url || this.form.url || this.form.el.dom.action;
7652 var p = this.getParams();
7654 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7660 getMethod : function(){
7661 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7664 getParams : function(){
7665 var bp = this.form.baseParams;
7666 var p = this.options.params;
7668 if(typeof p == "object"){
7669 p = Roo.urlEncode(Roo.applyIf(p, bp));
7670 }else if(typeof p == 'string' && bp){
7671 p += '&' + Roo.urlEncode(bp);
7674 p = Roo.urlEncode(bp);
7679 createCallback : function(){
7681 success: this.success,
7682 failure: this.failure,
7684 timeout: (this.form.timeout*1000),
7685 upload: this.form.fileUpload ? this.success : undefined
7690 Roo.form.Action.Submit = function(form, options){
7691 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7694 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7697 haveProgress : false,
7698 uploadComplete : false,
7700 // uploadProgress indicator.
7701 uploadProgress : function()
7703 if (!this.form.progressUrl) {
7707 if (!this.haveProgress) {
7708 Roo.MessageBox.progress("Uploading", "Uploading");
7710 if (this.uploadComplete) {
7711 Roo.MessageBox.hide();
7715 this.haveProgress = true;
7717 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7719 var c = new Roo.data.Connection();
7721 url : this.form.progressUrl,
7726 success : function(req){
7727 //console.log(data);
7731 rdata = Roo.decode(req.responseText)
7733 Roo.log("Invalid data from server..");
7737 if (!rdata || !rdata.success) {
7739 Roo.MessageBox.alert(Roo.encode(rdata));
7742 var data = rdata.data;
7744 if (this.uploadComplete) {
7745 Roo.MessageBox.hide();
7750 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7751 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7754 this.uploadProgress.defer(2000,this);
7757 failure: function(data) {
7758 Roo.log('progress url failed ');
7769 // run get Values on the form, so it syncs any secondary forms.
7770 this.form.getValues();
7772 var o = this.options;
7773 var method = this.getMethod();
7774 var isPost = method == 'POST';
7775 if(o.clientValidation === false || this.form.isValid()){
7777 if (this.form.progressUrl) {
7778 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7779 (new Date() * 1) + '' + Math.random());
7784 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7785 form:this.form.el.dom,
7786 url:this.getUrl(!isPost),
7788 params:isPost ? this.getParams() : null,
7789 isUpload: this.form.fileUpload
7792 this.uploadProgress();
7794 }else if (o.clientValidation !== false){ // client validation failed
7795 this.failureType = Roo.form.Action.CLIENT_INVALID;
7796 this.form.afterAction(this, false);
7800 success : function(response)
7802 this.uploadComplete= true;
7803 if (this.haveProgress) {
7804 Roo.MessageBox.hide();
7808 var result = this.processResponse(response);
7809 if(result === true || result.success){
7810 this.form.afterAction(this, true);
7814 this.form.markInvalid(result.errors);
7815 this.failureType = Roo.form.Action.SERVER_INVALID;
7817 this.form.afterAction(this, false);
7819 failure : function(response)
7821 this.uploadComplete= true;
7822 if (this.haveProgress) {
7823 Roo.MessageBox.hide();
7826 this.response = response;
7827 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7828 this.form.afterAction(this, false);
7831 handleResponse : function(response){
7832 if(this.form.errorReader){
7833 var rs = this.form.errorReader.read(response);
7836 for(var i = 0, len = rs.records.length; i < len; i++) {
7837 var r = rs.records[i];
7841 if(errors.length < 1){
7845 success : rs.success,
7851 ret = Roo.decode(response.responseText);
7855 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7865 Roo.form.Action.Load = function(form, options){
7866 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7867 this.reader = this.form.reader;
7870 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7875 Roo.Ajax.request(Roo.apply(
7876 this.createCallback(), {
7877 method:this.getMethod(),
7878 url:this.getUrl(false),
7879 params:this.getParams()
7883 success : function(response){
7885 var result = this.processResponse(response);
7886 if(result === true || !result.success || !result.data){
7887 this.failureType = Roo.form.Action.LOAD_FAILURE;
7888 this.form.afterAction(this, false);
7891 this.form.clearInvalid();
7892 this.form.setValues(result.data);
7893 this.form.afterAction(this, true);
7896 handleResponse : function(response){
7897 if(this.form.reader){
7898 var rs = this.form.reader.read(response);
7899 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7901 success : rs.success,
7905 return Roo.decode(response.responseText);
7909 Roo.form.Action.ACTION_TYPES = {
7910 'load' : Roo.form.Action.Load,
7911 'submit' : Roo.form.Action.Submit
7920 * @class Roo.bootstrap.Form
7921 * @extends Roo.bootstrap.Component
7922 * Bootstrap Form class
7923 * @cfg {String} method GET | POST (default POST)
7924 * @cfg {String} labelAlign top | left (default top)
7925 * @cfg {String} align left | right - for navbars
7926 * @cfg {Boolean} loadMask load mask when submit (default true)
7931 * @param {Object} config The config object
7935 Roo.bootstrap.Form = function(config){
7937 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7939 Roo.bootstrap.Form.popover.apply();
7943 * @event clientvalidation
7944 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7945 * @param {Form} this
7946 * @param {Boolean} valid true if the form has passed client-side validation
7948 clientvalidation: true,
7950 * @event beforeaction
7951 * Fires before any action is performed. Return false to cancel the action.
7952 * @param {Form} this
7953 * @param {Action} action The action to be performed
7957 * @event actionfailed
7958 * Fires when an action fails.
7959 * @param {Form} this
7960 * @param {Action} action The action that failed
7962 actionfailed : true,
7964 * @event actioncomplete
7965 * Fires when an action is completed.
7966 * @param {Form} this
7967 * @param {Action} action The action that completed
7969 actioncomplete : true
7973 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7976 * @cfg {String} method
7977 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7982 * The URL to use for form actions if one isn't supplied in the action options.
7985 * @cfg {Boolean} fileUpload
7986 * Set to true if this form is a file upload.
7990 * @cfg {Object} baseParams
7991 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7995 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7999 * @cfg {Sting} align (left|right) for navbar forms
8004 activeAction : null,
8007 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8008 * element by passing it or its id or mask the form itself by passing in true.
8011 waitMsgTarget : false,
8016 * @cfg {Boolean} errorMask (true|false) default false
8021 * @cfg {Number} maskOffset Default 100
8026 * @cfg {Boolean} maskBody
8030 getAutoCreate : function(){
8034 method : this.method || 'POST',
8035 id : this.id || Roo.id(),
8038 if (this.parent().xtype.match(/^Nav/)) {
8039 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8043 if (this.labelAlign == 'left' ) {
8044 cfg.cls += ' form-horizontal';
8050 initEvents : function()
8052 this.el.on('submit', this.onSubmit, this);
8053 // this was added as random key presses on the form where triggering form submit.
8054 this.el.on('keypress', function(e) {
8055 if (e.getCharCode() != 13) {
8058 // we might need to allow it for textareas.. and some other items.
8059 // check e.getTarget().
8061 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8065 Roo.log("keypress blocked");
8073 onSubmit : function(e){
8078 * Returns true if client-side validation on the form is successful.
8081 isValid : function(){
8082 var items = this.getItems();
8086 items.each(function(f){
8092 Roo.log('invalid field: ' + f.name);
8096 if(!target && f.el.isVisible(true)){
8102 if(this.errorMask && !valid){
8103 Roo.bootstrap.Form.popover.mask(this, target);
8110 * Returns true if any fields in this form have changed since their original load.
8113 isDirty : function(){
8115 var items = this.getItems();
8116 items.each(function(f){
8126 * Performs a predefined action (submit or load) or custom actions you define on this form.
8127 * @param {String} actionName The name of the action type
8128 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8129 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8130 * accept other config options):
8132 Property Type Description
8133 ---------------- --------------- ----------------------------------------------------------------------------------
8134 url String The url for the action (defaults to the form's url)
8135 method String The form method to use (defaults to the form's method, or POST if not defined)
8136 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8137 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8138 validate the form on the client (defaults to false)
8140 * @return {BasicForm} this
8142 doAction : function(action, options){
8143 if(typeof action == 'string'){
8144 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8146 if(this.fireEvent('beforeaction', this, action) !== false){
8147 this.beforeAction(action);
8148 action.run.defer(100, action);
8154 beforeAction : function(action){
8155 var o = action.options;
8160 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8162 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8165 // not really supported yet.. ??
8167 //if(this.waitMsgTarget === true){
8168 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8169 //}else if(this.waitMsgTarget){
8170 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8171 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8173 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8179 afterAction : function(action, success){
8180 this.activeAction = null;
8181 var o = action.options;
8186 Roo.get(document.body).unmask();
8192 //if(this.waitMsgTarget === true){
8193 // this.el.unmask();
8194 //}else if(this.waitMsgTarget){
8195 // this.waitMsgTarget.unmask();
8197 // Roo.MessageBox.updateProgress(1);
8198 // Roo.MessageBox.hide();
8205 Roo.callback(o.success, o.scope, [this, action]);
8206 this.fireEvent('actioncomplete', this, action);
8210 // failure condition..
8211 // we have a scenario where updates need confirming.
8212 // eg. if a locking scenario exists..
8213 // we look for { errors : { needs_confirm : true }} in the response.
8215 (typeof(action.result) != 'undefined') &&
8216 (typeof(action.result.errors) != 'undefined') &&
8217 (typeof(action.result.errors.needs_confirm) != 'undefined')
8220 Roo.log("not supported yet");
8223 Roo.MessageBox.confirm(
8224 "Change requires confirmation",
8225 action.result.errorMsg,
8230 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8240 Roo.callback(o.failure, o.scope, [this, action]);
8241 // show an error message if no failed handler is set..
8242 if (!this.hasListener('actionfailed')) {
8243 Roo.log("need to add dialog support");
8245 Roo.MessageBox.alert("Error",
8246 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8247 action.result.errorMsg :
8248 "Saving Failed, please check your entries or try again"
8253 this.fireEvent('actionfailed', this, action);
8258 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8259 * @param {String} id The value to search for
8262 findField : function(id){
8263 var items = this.getItems();
8264 var field = items.get(id);
8266 items.each(function(f){
8267 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8274 return field || null;
8277 * Mark fields in this form invalid in bulk.
8278 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8279 * @return {BasicForm} this
8281 markInvalid : function(errors){
8282 if(errors instanceof Array){
8283 for(var i = 0, len = errors.length; i < len; i++){
8284 var fieldError = errors[i];
8285 var f = this.findField(fieldError.id);
8287 f.markInvalid(fieldError.msg);
8293 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8294 field.markInvalid(errors[id]);
8298 //Roo.each(this.childForms || [], function (f) {
8299 // f.markInvalid(errors);
8306 * Set values for fields in this form in bulk.
8307 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8308 * @return {BasicForm} this
8310 setValues : function(values){
8311 if(values instanceof Array){ // array of objects
8312 for(var i = 0, len = values.length; i < len; i++){
8314 var f = this.findField(v.id);
8316 f.setValue(v.value);
8317 if(this.trackResetOnLoad){
8318 f.originalValue = f.getValue();
8322 }else{ // object hash
8325 if(typeof values[id] != 'function' && (field = this.findField(id))){
8327 if (field.setFromData &&
8329 field.displayField &&
8330 // combos' with local stores can
8331 // be queried via setValue()
8332 // to set their value..
8333 (field.store && !field.store.isLocal)
8337 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8338 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8339 field.setFromData(sd);
8341 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8343 field.setFromData(values);
8346 field.setValue(values[id]);
8350 if(this.trackResetOnLoad){
8351 field.originalValue = field.getValue();
8357 //Roo.each(this.childForms || [], function (f) {
8358 // f.setValues(values);
8365 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8366 * they are returned as an array.
8367 * @param {Boolean} asString
8370 getValues : function(asString){
8371 //if (this.childForms) {
8372 // copy values from the child forms
8373 // Roo.each(this.childForms, function (f) {
8374 // this.setValues(f.getValues());
8380 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8381 if(asString === true){
8384 return Roo.urlDecode(fs);
8388 * Returns the fields in this form as an object with key/value pairs.
8389 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8392 getFieldValues : function(with_hidden)
8394 var items = this.getItems();
8396 items.each(function(f){
8402 var v = f.getValue();
8404 if (f.inputType =='radio') {
8405 if (typeof(ret[f.getName()]) == 'undefined') {
8406 ret[f.getName()] = ''; // empty..
8409 if (!f.el.dom.checked) {
8417 if(f.xtype == 'MoneyField'){
8418 ret[f.currencyName] = f.getCurrency();
8421 // not sure if this supported any more..
8422 if ((typeof(v) == 'object') && f.getRawValue) {
8423 v = f.getRawValue() ; // dates..
8425 // combo boxes where name != hiddenName...
8426 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8427 ret[f.name] = f.getRawValue();
8429 ret[f.getName()] = v;
8436 * Clears all invalid messages in this form.
8437 * @return {BasicForm} this
8439 clearInvalid : function(){
8440 var items = this.getItems();
8442 items.each(function(f){
8451 * @return {BasicForm} this
8454 var items = this.getItems();
8455 items.each(function(f){
8459 Roo.each(this.childForms || [], function (f) {
8467 getItems : function()
8469 var r=new Roo.util.MixedCollection(false, function(o){
8470 return o.id || (o.id = Roo.id());
8472 var iter = function(el) {
8479 Roo.each(el.items,function(e) {
8488 hideFields : function(items)
8490 Roo.each(items, function(i){
8492 var f = this.findField(i);
8503 showFields : function(items)
8505 Roo.each(items, function(i){
8507 var f = this.findField(i);
8520 Roo.apply(Roo.bootstrap.Form, {
8547 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8548 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8549 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8550 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8553 this.maskEl.top.enableDisplayMode("block");
8554 this.maskEl.left.enableDisplayMode("block");
8555 this.maskEl.bottom.enableDisplayMode("block");
8556 this.maskEl.right.enableDisplayMode("block");
8558 this.toolTip = new Roo.bootstrap.Tooltip({
8559 cls : 'roo-form-error-popover',
8561 'left' : ['r-l', [-2,0], 'right'],
8562 'right' : ['l-r', [2,0], 'left'],
8563 'bottom' : ['tl-bl', [0,2], 'top'],
8564 'top' : [ 'bl-tl', [0,-2], 'bottom']
8568 this.toolTip.render(Roo.get(document.body));
8570 this.toolTip.el.enableDisplayMode("block");
8572 Roo.get(document.body).on('click', function(){
8576 Roo.get(document.body).on('touchstart', function(){
8580 this.isApplied = true
8583 mask : function(form, target)
8587 this.target = target;
8589 if(!this.form.errorMask || !target.el){
8593 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8595 Roo.log(scrollable);
8597 var ot = this.target.el.calcOffsetsTo(scrollable);
8599 var scrollTo = ot[1] - this.form.maskOffset;
8601 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8603 scrollable.scrollTo('top', scrollTo);
8605 var box = this.target.el.getBox();
8607 var zIndex = Roo.bootstrap.Modal.zIndex++;
8610 this.maskEl.top.setStyle('position', 'absolute');
8611 this.maskEl.top.setStyle('z-index', zIndex);
8612 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8613 this.maskEl.top.setLeft(0);
8614 this.maskEl.top.setTop(0);
8615 this.maskEl.top.show();
8617 this.maskEl.left.setStyle('position', 'absolute');
8618 this.maskEl.left.setStyle('z-index', zIndex);
8619 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8620 this.maskEl.left.setLeft(0);
8621 this.maskEl.left.setTop(box.y - this.padding);
8622 this.maskEl.left.show();
8624 this.maskEl.bottom.setStyle('position', 'absolute');
8625 this.maskEl.bottom.setStyle('z-index', zIndex);
8626 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8627 this.maskEl.bottom.setLeft(0);
8628 this.maskEl.bottom.setTop(box.bottom + this.padding);
8629 this.maskEl.bottom.show();
8631 this.maskEl.right.setStyle('position', 'absolute');
8632 this.maskEl.right.setStyle('z-index', zIndex);
8633 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8634 this.maskEl.right.setLeft(box.right + this.padding);
8635 this.maskEl.right.setTop(box.y - this.padding);
8636 this.maskEl.right.show();
8638 this.toolTip.bindEl = this.target.el;
8640 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8642 var tip = this.target.blankText;
8644 if(this.target.getValue() !== '' ) {
8646 if (this.target.invalidText.length) {
8647 tip = this.target.invalidText;
8648 } else if (this.target.regexText.length){
8649 tip = this.target.regexText;
8653 this.toolTip.show(tip);
8655 this.intervalID = window.setInterval(function() {
8656 Roo.bootstrap.Form.popover.unmask();
8659 window.onwheel = function(){ return false;};
8661 (function(){ this.isMasked = true; }).defer(500, this);
8667 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8671 this.maskEl.top.setStyle('position', 'absolute');
8672 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8673 this.maskEl.top.hide();
8675 this.maskEl.left.setStyle('position', 'absolute');
8676 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8677 this.maskEl.left.hide();
8679 this.maskEl.bottom.setStyle('position', 'absolute');
8680 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8681 this.maskEl.bottom.hide();
8683 this.maskEl.right.setStyle('position', 'absolute');
8684 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8685 this.maskEl.right.hide();
8687 this.toolTip.hide();
8689 this.toolTip.el.hide();
8691 window.onwheel = function(){ return true;};
8693 if(this.intervalID){
8694 window.clearInterval(this.intervalID);
8695 this.intervalID = false;
8698 this.isMasked = false;
8708 * Ext JS Library 1.1.1
8709 * Copyright(c) 2006-2007, Ext JS, LLC.
8711 * Originally Released Under LGPL - original licence link has changed is not relivant.
8714 * <script type="text/javascript">
8717 * @class Roo.form.VTypes
8718 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8721 Roo.form.VTypes = function(){
8722 // closure these in so they are only created once.
8723 var alpha = /^[a-zA-Z_]+$/;
8724 var alphanum = /^[a-zA-Z0-9_]+$/;
8725 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8726 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8728 // All these messages and functions are configurable
8731 * The function used to validate email addresses
8732 * @param {String} value The email address
8734 'email' : function(v){
8735 return email.test(v);
8738 * The error text to display when the email validation function returns false
8741 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8743 * The keystroke filter mask to be applied on email input
8746 'emailMask' : /[a-z0-9_\.\-@]/i,
8749 * The function used to validate URLs
8750 * @param {String} value The URL
8752 'url' : function(v){
8756 * The error text to display when the url validation function returns false
8759 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8762 * The function used to validate alpha values
8763 * @param {String} value The value
8765 'alpha' : function(v){
8766 return alpha.test(v);
8769 * The error text to display when the alpha validation function returns false
8772 'alphaText' : 'This field should only contain letters and _',
8774 * The keystroke filter mask to be applied on alpha input
8777 'alphaMask' : /[a-z_]/i,
8780 * The function used to validate alphanumeric values
8781 * @param {String} value The value
8783 'alphanum' : function(v){
8784 return alphanum.test(v);
8787 * The error text to display when the alphanumeric validation function returns false
8790 'alphanumText' : 'This field should only contain letters, numbers and _',
8792 * The keystroke filter mask to be applied on alphanumeric input
8795 'alphanumMask' : /[a-z0-9_]/i
8805 * @class Roo.bootstrap.Input
8806 * @extends Roo.bootstrap.Component
8807 * Bootstrap Input class
8808 * @cfg {Boolean} disabled is it disabled
8809 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8810 * @cfg {String} name name of the input
8811 * @cfg {string} fieldLabel - the label associated
8812 * @cfg {string} placeholder - placeholder to put in text.
8813 * @cfg {string} before - input group add on before
8814 * @cfg {string} after - input group add on after
8815 * @cfg {string} size - (lg|sm) or leave empty..
8816 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8817 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8818 * @cfg {Number} md colspan out of 12 for computer-sized screens
8819 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8820 * @cfg {string} value default value of the input
8821 * @cfg {Number} labelWidth set the width of label
8822 * @cfg {Number} labellg set the width of label (1-12)
8823 * @cfg {Number} labelmd set the width of label (1-12)
8824 * @cfg {Number} labelsm set the width of label (1-12)
8825 * @cfg {Number} labelxs set the width of label (1-12)
8826 * @cfg {String} labelAlign (top|left)
8827 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8828 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8829 * @cfg {String} indicatorpos (left|right) default left
8830 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8831 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8833 * @cfg {String} align (left|center|right) Default left
8834 * @cfg {Boolean} forceFeedback (true|false) Default false
8837 * Create a new Input
8838 * @param {Object} config The config object
8841 Roo.bootstrap.Input = function(config){
8843 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8848 * Fires when this field receives input focus.
8849 * @param {Roo.form.Field} this
8854 * Fires when this field loses input focus.
8855 * @param {Roo.form.Field} this
8860 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8861 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8862 * @param {Roo.form.Field} this
8863 * @param {Roo.EventObject} e The event object
8868 * Fires just before the field blurs if the field value has changed.
8869 * @param {Roo.form.Field} this
8870 * @param {Mixed} newValue The new value
8871 * @param {Mixed} oldValue The original value
8876 * Fires after the field has been marked as invalid.
8877 * @param {Roo.form.Field} this
8878 * @param {String} msg The validation message
8883 * Fires after the field has been validated with no errors.
8884 * @param {Roo.form.Field} this
8889 * Fires after the key up
8890 * @param {Roo.form.Field} this
8891 * @param {Roo.EventObject} e The event Object
8897 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8899 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8900 automatic validation (defaults to "keyup").
8902 validationEvent : "keyup",
8904 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8906 validateOnBlur : true,
8908 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8910 validationDelay : 250,
8912 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8914 focusClass : "x-form-focus", // not needed???
8918 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8920 invalidClass : "has-warning",
8923 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8925 validClass : "has-success",
8928 * @cfg {Boolean} hasFeedback (true|false) default true
8933 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8935 invalidFeedbackClass : "glyphicon-warning-sign",
8938 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8940 validFeedbackClass : "glyphicon-ok",
8943 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8945 selectOnFocus : false,
8948 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8952 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8957 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8959 disableKeyFilter : false,
8962 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8966 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8970 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8972 blankText : "Please complete this mandatory field",
8975 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8979 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8981 maxLength : Number.MAX_VALUE,
8983 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8985 minLengthText : "The minimum length for this field is {0}",
8987 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8989 maxLengthText : "The maximum length for this field is {0}",
8993 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8994 * If available, this function will be called only after the basic validators all return true, and will be passed the
8995 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8999 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9000 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9001 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9005 * @cfg {String} regexText -- Depricated - use Invalid Text
9010 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9016 autocomplete: false,
9035 formatedValue : false,
9036 forceFeedback : false,
9038 indicatorpos : 'left',
9048 parentLabelAlign : function()
9051 while (parent.parent()) {
9052 parent = parent.parent();
9053 if (typeof(parent.labelAlign) !='undefined') {
9054 return parent.labelAlign;
9061 getAutoCreate : function()
9063 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9069 if(this.inputType != 'hidden'){
9070 cfg.cls = 'form-group' //input-group
9076 type : this.inputType,
9078 cls : 'form-control',
9079 placeholder : this.placeholder || '',
9080 autocomplete : this.autocomplete || 'new-password'
9083 if(this.capture.length){
9084 input.capture = this.capture;
9087 if(this.accept.length){
9088 input.accept = this.accept + "/*";
9092 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9095 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9096 input.maxLength = this.maxLength;
9099 if (this.disabled) {
9100 input.disabled=true;
9103 if (this.readOnly) {
9104 input.readonly=true;
9108 input.name = this.name;
9112 input.cls += ' input-' + this.size;
9116 ['xs','sm','md','lg'].map(function(size){
9117 if (settings[size]) {
9118 cfg.cls += ' col-' + size + '-' + settings[size];
9122 var inputblock = input;
9126 cls: 'glyphicon form-control-feedback'
9129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9132 cls : 'has-feedback',
9140 if (this.before || this.after) {
9143 cls : 'input-group',
9147 if (this.before && typeof(this.before) == 'string') {
9149 inputblock.cn.push({
9151 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9155 if (this.before && typeof(this.before) == 'object') {
9156 this.before = Roo.factory(this.before);
9158 inputblock.cn.push({
9160 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9161 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9165 inputblock.cn.push(input);
9167 if (this.after && typeof(this.after) == 'string') {
9168 inputblock.cn.push({
9170 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9174 if (this.after && typeof(this.after) == 'object') {
9175 this.after = Roo.factory(this.after);
9177 inputblock.cn.push({
9179 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9180 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9184 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9185 inputblock.cls += ' has-feedback';
9186 inputblock.cn.push(feedback);
9191 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9192 tooltip : 'This field is required'
9194 if (Roo.bootstrap.version == 4) {
9197 style : 'display-none'
9200 if (align ==='left' && this.fieldLabel.length) {
9202 cfg.cls += ' roo-form-group-label-left row';
9209 cls : 'control-label col-form-label',
9210 html : this.fieldLabel
9221 var labelCfg = cfg.cn[1];
9222 var contentCfg = cfg.cn[2];
9224 if(this.indicatorpos == 'right'){
9229 cls : 'control-label col-form-label',
9233 html : this.fieldLabel
9247 labelCfg = cfg.cn[0];
9248 contentCfg = cfg.cn[1];
9252 if(this.labelWidth > 12){
9253 labelCfg.style = "width: " + this.labelWidth + 'px';
9256 if(this.labelWidth < 13 && this.labelmd == 0){
9257 this.labelmd = this.labelWidth;
9260 if(this.labellg > 0){
9261 labelCfg.cls += ' col-lg-' + this.labellg;
9262 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9265 if(this.labelmd > 0){
9266 labelCfg.cls += ' col-md-' + this.labelmd;
9267 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9270 if(this.labelsm > 0){
9271 labelCfg.cls += ' col-sm-' + this.labelsm;
9272 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9275 if(this.labelxs > 0){
9276 labelCfg.cls += ' col-xs-' + this.labelxs;
9277 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9281 } else if ( this.fieldLabel.length) {
9286 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9287 tooltip : 'This field is required'
9291 //cls : 'input-group-addon',
9292 html : this.fieldLabel
9300 if(this.indicatorpos == 'right'){
9305 //cls : 'input-group-addon',
9306 html : this.fieldLabel
9311 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9312 tooltip : 'This field is required'
9332 if (this.parentType === 'Navbar' && this.parent().bar) {
9333 cfg.cls += ' navbar-form';
9336 if (this.parentType === 'NavGroup') {
9337 cfg.cls += ' navbar-form';
9345 * return the real input element.
9347 inputEl: function ()
9349 return this.el.select('input.form-control',true).first();
9352 tooltipEl : function()
9354 return this.inputEl();
9357 indicatorEl : function()
9359 if (Roo.bootstrap.version == 4) {
9360 return false; // not enabled in v4 yet.
9363 var indicator = this.el.select('i.roo-required-indicator',true).first();
9373 setDisabled : function(v)
9375 var i = this.inputEl().dom;
9377 i.removeAttribute('disabled');
9381 i.setAttribute('disabled','true');
9383 initEvents : function()
9386 this.inputEl().on("keydown" , this.fireKey, this);
9387 this.inputEl().on("focus", this.onFocus, this);
9388 this.inputEl().on("blur", this.onBlur, this);
9390 this.inputEl().relayEvent('keyup', this);
9392 this.indicator = this.indicatorEl();
9395 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9398 // reference to original value for reset
9399 this.originalValue = this.getValue();
9400 //Roo.form.TextField.superclass.initEvents.call(this);
9401 if(this.validationEvent == 'keyup'){
9402 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9403 this.inputEl().on('keyup', this.filterValidation, this);
9405 else if(this.validationEvent !== false){
9406 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9409 if(this.selectOnFocus){
9410 this.on("focus", this.preFocus, this);
9413 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9414 this.inputEl().on("keypress", this.filterKeys, this);
9416 this.inputEl().relayEvent('keypress', this);
9419 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9420 this.el.on("click", this.autoSize, this);
9423 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9424 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9427 if (typeof(this.before) == 'object') {
9428 this.before.render(this.el.select('.roo-input-before',true).first());
9430 if (typeof(this.after) == 'object') {
9431 this.after.render(this.el.select('.roo-input-after',true).first());
9434 this.inputEl().on('change', this.onChange, this);
9437 filterValidation : function(e){
9438 if(!e.isNavKeyPress()){
9439 this.validationTask.delay(this.validationDelay);
9443 * Validates the field value
9444 * @return {Boolean} True if the value is valid, else false
9446 validate : function(){
9447 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9448 if(this.disabled || this.validateValue(this.getRawValue())){
9459 * Validates a value according to the field's validation rules and marks the field as invalid
9460 * if the validation fails
9461 * @param {Mixed} value The value to validate
9462 * @return {Boolean} True if the value is valid, else false
9464 validateValue : function(value)
9466 if(this.getVisibilityEl().hasClass('hidden')){
9470 if(value.length < 1) { // if it's blank
9471 if(this.allowBlank){
9477 if(value.length < this.minLength){
9480 if(value.length > this.maxLength){
9484 var vt = Roo.form.VTypes;
9485 if(!vt[this.vtype](value, this)){
9489 if(typeof this.validator == "function"){
9490 var msg = this.validator(value);
9494 if (typeof(msg) == 'string') {
9495 this.invalidText = msg;
9499 if(this.regex && !this.regex.test(value)){
9507 fireKey : function(e){
9508 //Roo.log('field ' + e.getKey());
9509 if(e.isNavKeyPress()){
9510 this.fireEvent("specialkey", this, e);
9513 focus : function (selectText){
9515 this.inputEl().focus();
9516 if(selectText === true){
9517 this.inputEl().dom.select();
9523 onFocus : function(){
9524 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9525 // this.el.addClass(this.focusClass);
9528 this.hasFocus = true;
9529 this.startValue = this.getValue();
9530 this.fireEvent("focus", this);
9534 beforeBlur : Roo.emptyFn,
9538 onBlur : function(){
9540 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9541 //this.el.removeClass(this.focusClass);
9543 this.hasFocus = false;
9544 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9547 var v = this.getValue();
9548 if(String(v) !== String(this.startValue)){
9549 this.fireEvent('change', this, v, this.startValue);
9551 this.fireEvent("blur", this);
9554 onChange : function(e)
9556 var v = this.getValue();
9557 if(String(v) !== String(this.startValue)){
9558 this.fireEvent('change', this, v, this.startValue);
9564 * Resets the current field value to the originally loaded value and clears any validation messages
9567 this.setValue(this.originalValue);
9571 * Returns the name of the field
9572 * @return {Mixed} name The name field
9574 getName: function(){
9578 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9579 * @return {Mixed} value The field value
9581 getValue : function(){
9583 var v = this.inputEl().getValue();
9588 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9589 * @return {Mixed} value The field value
9591 getRawValue : function(){
9592 var v = this.inputEl().getValue();
9598 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9599 * @param {Mixed} value The value to set
9601 setRawValue : function(v){
9602 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9605 selectText : function(start, end){
9606 var v = this.getRawValue();
9608 start = start === undefined ? 0 : start;
9609 end = end === undefined ? v.length : end;
9610 var d = this.inputEl().dom;
9611 if(d.setSelectionRange){
9612 d.setSelectionRange(start, end);
9613 }else if(d.createTextRange){
9614 var range = d.createTextRange();
9615 range.moveStart("character", start);
9616 range.moveEnd("character", v.length-end);
9623 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9624 * @param {Mixed} value The value to set
9626 setValue : function(v){
9629 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9635 processValue : function(value){
9636 if(this.stripCharsRe){
9637 var newValue = value.replace(this.stripCharsRe, '');
9638 if(newValue !== value){
9639 this.setRawValue(newValue);
9646 preFocus : function(){
9648 if(this.selectOnFocus){
9649 this.inputEl().dom.select();
9652 filterKeys : function(e){
9654 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9657 var c = e.getCharCode(), cc = String.fromCharCode(c);
9658 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9661 if(!this.maskRe.test(cc)){
9666 * Clear any invalid styles/messages for this field
9668 clearInvalid : function(){
9670 if(!this.el || this.preventMark){ // not rendered
9675 this.el.removeClass(this.invalidClass);
9677 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9679 var feedback = this.el.select('.form-control-feedback', true).first();
9682 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9688 this.indicator.removeClass('visible');
9689 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9692 this.fireEvent('valid', this);
9696 * Mark this field as valid
9698 markValid : function()
9700 if(!this.el || this.preventMark){ // not rendered...
9704 this.el.removeClass([this.invalidClass, this.validClass]);
9706 var feedback = this.el.select('.form-control-feedback', true).first();
9709 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9713 this.indicator.removeClass('visible');
9714 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9721 if(this.allowBlank && !this.getRawValue().length){
9725 this.el.addClass(this.validClass);
9727 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9729 var feedback = this.el.select('.form-control-feedback', true).first();
9732 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9733 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9738 this.fireEvent('valid', this);
9742 * Mark this field as invalid
9743 * @param {String} msg The validation message
9745 markInvalid : function(msg)
9747 if(!this.el || this.preventMark){ // not rendered
9751 this.el.removeClass([this.invalidClass, this.validClass]);
9753 var feedback = this.el.select('.form-control-feedback', true).first();
9756 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9763 if(this.allowBlank && !this.getRawValue().length){
9768 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9769 this.indicator.addClass('visible');
9772 this.el.addClass(this.invalidClass);
9774 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9776 var feedback = this.el.select('.form-control-feedback', true).first();
9779 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9781 if(this.getValue().length || this.forceFeedback){
9782 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9789 this.fireEvent('invalid', this, msg);
9792 SafariOnKeyDown : function(event)
9794 // this is a workaround for a password hang bug on chrome/ webkit.
9795 if (this.inputEl().dom.type != 'password') {
9799 var isSelectAll = false;
9801 if(this.inputEl().dom.selectionEnd > 0){
9802 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9804 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9805 event.preventDefault();
9810 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9812 event.preventDefault();
9813 // this is very hacky as keydown always get's upper case.
9815 var cc = String.fromCharCode(event.getCharCode());
9816 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9820 adjustWidth : function(tag, w){
9821 tag = tag.toLowerCase();
9822 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9823 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9827 if(tag == 'textarea'){
9830 }else if(Roo.isOpera){
9834 if(tag == 'textarea'){
9842 setFieldLabel : function(v)
9848 if(this.indicatorEl()){
9849 var ar = this.el.select('label > span',true);
9851 if (ar.elements.length) {
9852 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9853 this.fieldLabel = v;
9857 var br = this.el.select('label',true);
9859 if(br.elements.length) {
9860 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9861 this.fieldLabel = v;
9865 Roo.log('Cannot Found any of label > span || label in input');
9869 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9870 this.fieldLabel = v;
9885 * @class Roo.bootstrap.TextArea
9886 * @extends Roo.bootstrap.Input
9887 * Bootstrap TextArea class
9888 * @cfg {Number} cols Specifies the visible width of a text area
9889 * @cfg {Number} rows Specifies the visible number of lines in a text area
9890 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9891 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9892 * @cfg {string} html text
9895 * Create a new TextArea
9896 * @param {Object} config The config object
9899 Roo.bootstrap.TextArea = function(config){
9900 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9904 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9914 getAutoCreate : function(){
9916 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9922 if(this.inputType != 'hidden'){
9923 cfg.cls = 'form-group' //input-group
9931 value : this.value || '',
9932 html: this.html || '',
9933 cls : 'form-control',
9934 placeholder : this.placeholder || ''
9938 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9939 input.maxLength = this.maxLength;
9943 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9947 input.cols = this.cols;
9950 if (this.readOnly) {
9951 input.readonly = true;
9955 input.name = this.name;
9959 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9963 ['xs','sm','md','lg'].map(function(size){
9964 if (settings[size]) {
9965 cfg.cls += ' col-' + size + '-' + settings[size];
9969 var inputblock = input;
9971 if(this.hasFeedback && !this.allowBlank){
9975 cls: 'glyphicon form-control-feedback'
9979 cls : 'has-feedback',
9988 if (this.before || this.after) {
9991 cls : 'input-group',
9995 inputblock.cn.push({
9997 cls : 'input-group-addon',
10002 inputblock.cn.push(input);
10004 if(this.hasFeedback && !this.allowBlank){
10005 inputblock.cls += ' has-feedback';
10006 inputblock.cn.push(feedback);
10010 inputblock.cn.push({
10012 cls : 'input-group-addon',
10019 if (align ==='left' && this.fieldLabel.length) {
10024 cls : 'control-label',
10025 html : this.fieldLabel
10036 if(this.labelWidth > 12){
10037 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10040 if(this.labelWidth < 13 && this.labelmd == 0){
10041 this.labelmd = this.labelWidth;
10044 if(this.labellg > 0){
10045 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10046 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10049 if(this.labelmd > 0){
10050 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10051 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10054 if(this.labelsm > 0){
10055 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10056 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10059 if(this.labelxs > 0){
10060 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10061 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10064 } else if ( this.fieldLabel.length) {
10069 //cls : 'input-group-addon',
10070 html : this.fieldLabel
10088 if (this.disabled) {
10089 input.disabled=true;
10096 * return the real textarea element.
10098 inputEl: function ()
10100 return this.el.select('textarea.form-control',true).first();
10104 * Clear any invalid styles/messages for this field
10106 clearInvalid : function()
10109 if(!this.el || this.preventMark){ // not rendered
10113 var label = this.el.select('label', true).first();
10114 var icon = this.el.select('i.fa-star', true).first();
10120 this.el.removeClass(this.invalidClass);
10122 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10124 var feedback = this.el.select('.form-control-feedback', true).first();
10127 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10132 this.fireEvent('valid', this);
10136 * Mark this field as valid
10138 markValid : function()
10140 if(!this.el || this.preventMark){ // not rendered
10144 this.el.removeClass([this.invalidClass, this.validClass]);
10146 var feedback = this.el.select('.form-control-feedback', true).first();
10149 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10152 if(this.disabled || this.allowBlank){
10156 var label = this.el.select('label', true).first();
10157 var icon = this.el.select('i.fa-star', true).first();
10163 this.el.addClass(this.validClass);
10165 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10167 var feedback = this.el.select('.form-control-feedback', true).first();
10170 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10171 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10176 this.fireEvent('valid', this);
10180 * Mark this field as invalid
10181 * @param {String} msg The validation message
10183 markInvalid : function(msg)
10185 if(!this.el || this.preventMark){ // not rendered
10189 this.el.removeClass([this.invalidClass, this.validClass]);
10191 var feedback = this.el.select('.form-control-feedback', true).first();
10194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10197 if(this.disabled || this.allowBlank){
10201 var label = this.el.select('label', true).first();
10202 var icon = this.el.select('i.fa-star', true).first();
10204 if(!this.getValue().length && label && !icon){
10205 this.el.createChild({
10207 cls : 'text-danger fa fa-lg fa-star',
10208 tooltip : 'This field is required',
10209 style : 'margin-right:5px;'
10213 this.el.addClass(this.invalidClass);
10215 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10217 var feedback = this.el.select('.form-control-feedback', true).first();
10220 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10222 if(this.getValue().length || this.forceFeedback){
10223 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10230 this.fireEvent('invalid', this, msg);
10238 * trigger field - base class for combo..
10243 * @class Roo.bootstrap.TriggerField
10244 * @extends Roo.bootstrap.Input
10245 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10246 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10247 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10248 * for which you can provide a custom implementation. For example:
10250 var trigger = new Roo.bootstrap.TriggerField();
10251 trigger.onTriggerClick = myTriggerFn;
10252 trigger.applyTo('my-field');
10255 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10256 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10257 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10258 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10259 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10262 * Create a new TriggerField.
10263 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10264 * to the base TextField)
10266 Roo.bootstrap.TriggerField = function(config){
10267 this.mimicing = false;
10268 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10271 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10273 * @cfg {String} triggerClass A CSS class to apply to the trigger
10276 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10281 * @cfg {Boolean} removable (true|false) special filter default false
10285 /** @cfg {Boolean} grow @hide */
10286 /** @cfg {Number} growMin @hide */
10287 /** @cfg {Number} growMax @hide */
10293 autoSize: Roo.emptyFn,
10297 deferHeight : true,
10300 actionMode : 'wrap',
10305 getAutoCreate : function(){
10307 var align = this.labelAlign || this.parentLabelAlign();
10312 cls: 'form-group' //input-group
10319 type : this.inputType,
10320 cls : 'form-control',
10321 autocomplete: 'new-password',
10322 placeholder : this.placeholder || ''
10326 input.name = this.name;
10329 input.cls += ' input-' + this.size;
10332 if (this.disabled) {
10333 input.disabled=true;
10336 var inputblock = input;
10338 if(this.hasFeedback && !this.allowBlank){
10342 cls: 'glyphicon form-control-feedback'
10345 if(this.removable && !this.editable && !this.tickable){
10347 cls : 'has-feedback',
10353 cls : 'roo-combo-removable-btn close'
10360 cls : 'has-feedback',
10369 if(this.removable && !this.editable && !this.tickable){
10371 cls : 'roo-removable',
10377 cls : 'roo-combo-removable-btn close'
10384 if (this.before || this.after) {
10387 cls : 'input-group',
10391 inputblock.cn.push({
10393 cls : 'input-group-addon input-group-prepend input-group-text',
10398 inputblock.cn.push(input);
10400 if(this.hasFeedback && !this.allowBlank){
10401 inputblock.cls += ' has-feedback';
10402 inputblock.cn.push(feedback);
10406 inputblock.cn.push({
10408 cls : 'input-group-addon input-group-append input-group-text',
10417 var ibwrap = inputblock;
10422 cls: 'roo-select2-choices',
10426 cls: 'roo-select2-search-field',
10438 cls: 'roo-select2-container input-group',
10443 cls: 'form-hidden-field'
10449 if(!this.multiple && this.showToggleBtn){
10455 if (this.caret != false) {
10458 cls: 'fa fa-' + this.caret
10465 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10470 cls: 'combobox-clear',
10484 combobox.cls += ' roo-select2-container-multi';
10488 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10489 tooltip : 'This field is required'
10491 if (Roo.bootstrap.version == 4) {
10494 style : 'display:none'
10499 if (align ==='left' && this.fieldLabel.length) {
10501 cfg.cls += ' roo-form-group-label-left row';
10508 cls : 'control-label',
10509 html : this.fieldLabel
10521 var labelCfg = cfg.cn[1];
10522 var contentCfg = cfg.cn[2];
10524 if(this.indicatorpos == 'right'){
10529 cls : 'control-label',
10533 html : this.fieldLabel
10547 labelCfg = cfg.cn[0];
10548 contentCfg = cfg.cn[1];
10551 if(this.labelWidth > 12){
10552 labelCfg.style = "width: " + this.labelWidth + 'px';
10555 if(this.labelWidth < 13 && this.labelmd == 0){
10556 this.labelmd = this.labelWidth;
10559 if(this.labellg > 0){
10560 labelCfg.cls += ' col-lg-' + this.labellg;
10561 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10564 if(this.labelmd > 0){
10565 labelCfg.cls += ' col-md-' + this.labelmd;
10566 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10569 if(this.labelsm > 0){
10570 labelCfg.cls += ' col-sm-' + this.labelsm;
10571 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10574 if(this.labelxs > 0){
10575 labelCfg.cls += ' col-xs-' + this.labelxs;
10576 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10579 } else if ( this.fieldLabel.length) {
10580 // Roo.log(" label");
10585 //cls : 'input-group-addon',
10586 html : this.fieldLabel
10594 if(this.indicatorpos == 'right'){
10602 html : this.fieldLabel
10616 // Roo.log(" no label && no align");
10623 ['xs','sm','md','lg'].map(function(size){
10624 if (settings[size]) {
10625 cfg.cls += ' col-' + size + '-' + settings[size];
10636 onResize : function(w, h){
10637 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10638 // if(typeof w == 'number'){
10639 // var x = w - this.trigger.getWidth();
10640 // this.inputEl().setWidth(this.adjustWidth('input', x));
10641 // this.trigger.setStyle('left', x+'px');
10646 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10649 getResizeEl : function(){
10650 return this.inputEl();
10654 getPositionEl : function(){
10655 return this.inputEl();
10659 alignErrorIcon : function(){
10660 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10664 initEvents : function(){
10668 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10669 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10670 if(!this.multiple && this.showToggleBtn){
10671 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10672 if(this.hideTrigger){
10673 this.trigger.setDisplayed(false);
10675 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10679 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10682 if(this.removable && !this.editable && !this.tickable){
10683 var close = this.closeTriggerEl();
10686 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10687 close.on('click', this.removeBtnClick, this, close);
10691 //this.trigger.addClassOnOver('x-form-trigger-over');
10692 //this.trigger.addClassOnClick('x-form-trigger-click');
10695 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10699 closeTriggerEl : function()
10701 var close = this.el.select('.roo-combo-removable-btn', true).first();
10702 return close ? close : false;
10705 removeBtnClick : function(e, h, el)
10707 e.preventDefault();
10709 if(this.fireEvent("remove", this) !== false){
10711 this.fireEvent("afterremove", this)
10715 createList : function()
10717 this.list = Roo.get(document.body).createChild({
10718 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10719 cls: 'typeahead typeahead-long dropdown-menu',
10720 style: 'display:none'
10723 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10728 initTrigger : function(){
10733 onDestroy : function(){
10735 this.trigger.removeAllListeners();
10736 // this.trigger.remove();
10739 // this.wrap.remove();
10741 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10745 onFocus : function(){
10746 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10748 if(!this.mimicing){
10749 this.wrap.addClass('x-trigger-wrap-focus');
10750 this.mimicing = true;
10751 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10752 if(this.monitorTab){
10753 this.el.on("keydown", this.checkTab, this);
10760 checkTab : function(e){
10761 if(e.getKey() == e.TAB){
10762 this.triggerBlur();
10767 onBlur : function(){
10772 mimicBlur : function(e, t){
10774 if(!this.wrap.contains(t) && this.validateBlur()){
10775 this.triggerBlur();
10781 triggerBlur : function(){
10782 this.mimicing = false;
10783 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10784 if(this.monitorTab){
10785 this.el.un("keydown", this.checkTab, this);
10787 //this.wrap.removeClass('x-trigger-wrap-focus');
10788 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10792 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10793 validateBlur : function(e, t){
10798 onDisable : function(){
10799 this.inputEl().dom.disabled = true;
10800 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10802 // this.wrap.addClass('x-item-disabled');
10807 onEnable : function(){
10808 this.inputEl().dom.disabled = false;
10809 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10811 // this.el.removeClass('x-item-disabled');
10816 onShow : function(){
10817 var ae = this.getActionEl();
10820 ae.dom.style.display = '';
10821 ae.dom.style.visibility = 'visible';
10827 onHide : function(){
10828 var ae = this.getActionEl();
10829 ae.dom.style.display = 'none';
10833 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10834 * by an implementing function.
10836 * @param {EventObject} e
10838 onTriggerClick : Roo.emptyFn
10842 * Ext JS Library 1.1.1
10843 * Copyright(c) 2006-2007, Ext JS, LLC.
10845 * Originally Released Under LGPL - original licence link has changed is not relivant.
10848 * <script type="text/javascript">
10853 * @class Roo.data.SortTypes
10855 * Defines the default sorting (casting?) comparison functions used when sorting data.
10857 Roo.data.SortTypes = {
10859 * Default sort that does nothing
10860 * @param {Mixed} s The value being converted
10861 * @return {Mixed} The comparison value
10863 none : function(s){
10868 * The regular expression used to strip tags
10872 stripTagsRE : /<\/?[^>]+>/gi,
10875 * Strips all HTML tags to sort on text only
10876 * @param {Mixed} s The value being converted
10877 * @return {String} The comparison value
10879 asText : function(s){
10880 return String(s).replace(this.stripTagsRE, "");
10884 * Strips all HTML tags to sort on text only - Case insensitive
10885 * @param {Mixed} s The value being converted
10886 * @return {String} The comparison value
10888 asUCText : function(s){
10889 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10893 * Case insensitive string
10894 * @param {Mixed} s The value being converted
10895 * @return {String} The comparison value
10897 asUCString : function(s) {
10898 return String(s).toUpperCase();
10903 * @param {Mixed} s The value being converted
10904 * @return {Number} The comparison value
10906 asDate : function(s) {
10910 if(s instanceof Date){
10911 return s.getTime();
10913 return Date.parse(String(s));
10918 * @param {Mixed} s The value being converted
10919 * @return {Float} The comparison value
10921 asFloat : function(s) {
10922 var val = parseFloat(String(s).replace(/,/g, ""));
10931 * @param {Mixed} s The value being converted
10932 * @return {Number} The comparison value
10934 asInt : function(s) {
10935 var val = parseInt(String(s).replace(/,/g, ""));
10943 * Ext JS Library 1.1.1
10944 * Copyright(c) 2006-2007, Ext JS, LLC.
10946 * Originally Released Under LGPL - original licence link has changed is not relivant.
10949 * <script type="text/javascript">
10953 * @class Roo.data.Record
10954 * Instances of this class encapsulate both record <em>definition</em> information, and record
10955 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10956 * to access Records cached in an {@link Roo.data.Store} object.<br>
10958 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10959 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10962 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10964 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10965 * {@link #create}. The parameters are the same.
10966 * @param {Array} data An associative Array of data values keyed by the field name.
10967 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10968 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10969 * not specified an integer id is generated.
10971 Roo.data.Record = function(data, id){
10972 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10977 * Generate a constructor for a specific record layout.
10978 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10979 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10980 * Each field definition object may contain the following properties: <ul>
10981 * <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,
10982 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10983 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10984 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10985 * is being used, then this is a string containing the javascript expression to reference the data relative to
10986 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10987 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10988 * this may be omitted.</p></li>
10989 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10990 * <ul><li>auto (Default, implies no conversion)</li>
10995 * <li>date</li></ul></p></li>
10996 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10997 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10998 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10999 * by the Reader into an object that will be stored in the Record. It is passed the
11000 * following parameters:<ul>
11001 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11003 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11005 * <br>usage:<br><pre><code>
11006 var TopicRecord = Roo.data.Record.create(
11007 {name: 'title', mapping: 'topic_title'},
11008 {name: 'author', mapping: 'username'},
11009 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11010 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11011 {name: 'lastPoster', mapping: 'user2'},
11012 {name: 'excerpt', mapping: 'post_text'}
11015 var myNewRecord = new TopicRecord({
11016 title: 'Do my job please',
11019 lastPost: new Date(),
11020 lastPoster: 'Animal',
11021 excerpt: 'No way dude!'
11023 myStore.add(myNewRecord);
11028 Roo.data.Record.create = function(o){
11029 var f = function(){
11030 f.superclass.constructor.apply(this, arguments);
11032 Roo.extend(f, Roo.data.Record);
11033 var p = f.prototype;
11034 p.fields = new Roo.util.MixedCollection(false, function(field){
11037 for(var i = 0, len = o.length; i < len; i++){
11038 p.fields.add(new Roo.data.Field(o[i]));
11040 f.getField = function(name){
11041 return p.fields.get(name);
11046 Roo.data.Record.AUTO_ID = 1000;
11047 Roo.data.Record.EDIT = 'edit';
11048 Roo.data.Record.REJECT = 'reject';
11049 Roo.data.Record.COMMIT = 'commit';
11051 Roo.data.Record.prototype = {
11053 * Readonly flag - true if this record has been modified.
11062 join : function(store){
11063 this.store = store;
11067 * Set the named field to the specified value.
11068 * @param {String} name The name of the field to set.
11069 * @param {Object} value The value to set the field to.
11071 set : function(name, value){
11072 if(this.data[name] == value){
11076 if(!this.modified){
11077 this.modified = {};
11079 if(typeof this.modified[name] == 'undefined'){
11080 this.modified[name] = this.data[name];
11082 this.data[name] = value;
11083 if(!this.editing && this.store){
11084 this.store.afterEdit(this);
11089 * Get the value of the named field.
11090 * @param {String} name The name of the field to get the value of.
11091 * @return {Object} The value of the field.
11093 get : function(name){
11094 return this.data[name];
11098 beginEdit : function(){
11099 this.editing = true;
11100 this.modified = {};
11104 cancelEdit : function(){
11105 this.editing = false;
11106 delete this.modified;
11110 endEdit : function(){
11111 this.editing = false;
11112 if(this.dirty && this.store){
11113 this.store.afterEdit(this);
11118 * Usually called by the {@link Roo.data.Store} which owns the Record.
11119 * Rejects all changes made to the Record since either creation, or the last commit operation.
11120 * Modified fields are reverted to their original values.
11122 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11123 * of reject operations.
11125 reject : function(){
11126 var m = this.modified;
11128 if(typeof m[n] != "function"){
11129 this.data[n] = m[n];
11132 this.dirty = false;
11133 delete this.modified;
11134 this.editing = false;
11136 this.store.afterReject(this);
11141 * Usually called by the {@link Roo.data.Store} which owns the Record.
11142 * Commits all changes made to the Record since either creation, or the last commit operation.
11144 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11145 * of commit operations.
11147 commit : function(){
11148 this.dirty = false;
11149 delete this.modified;
11150 this.editing = false;
11152 this.store.afterCommit(this);
11157 hasError : function(){
11158 return this.error != null;
11162 clearError : function(){
11167 * Creates a copy of this record.
11168 * @param {String} id (optional) A new record id if you don't want to use this record's id
11171 copy : function(newId) {
11172 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11176 * Ext JS Library 1.1.1
11177 * Copyright(c) 2006-2007, Ext JS, LLC.
11179 * Originally Released Under LGPL - original licence link has changed is not relivant.
11182 * <script type="text/javascript">
11188 * @class Roo.data.Store
11189 * @extends Roo.util.Observable
11190 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11191 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11193 * 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
11194 * has no knowledge of the format of the data returned by the Proxy.<br>
11196 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11197 * instances from the data object. These records are cached and made available through accessor functions.
11199 * Creates a new Store.
11200 * @param {Object} config A config object containing the objects needed for the Store to access data,
11201 * and read the data into Records.
11203 Roo.data.Store = function(config){
11204 this.data = new Roo.util.MixedCollection(false);
11205 this.data.getKey = function(o){
11208 this.baseParams = {};
11210 this.paramNames = {
11215 "multisort" : "_multisort"
11218 if(config && config.data){
11219 this.inlineData = config.data;
11220 delete config.data;
11223 Roo.apply(this, config);
11225 if(this.reader){ // reader passed
11226 this.reader = Roo.factory(this.reader, Roo.data);
11227 this.reader.xmodule = this.xmodule || false;
11228 if(!this.recordType){
11229 this.recordType = this.reader.recordType;
11231 if(this.reader.onMetaChange){
11232 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11236 if(this.recordType){
11237 this.fields = this.recordType.prototype.fields;
11239 this.modified = [];
11243 * @event datachanged
11244 * Fires when the data cache has changed, and a widget which is using this Store
11245 * as a Record cache should refresh its view.
11246 * @param {Store} this
11248 datachanged : true,
11250 * @event metachange
11251 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11252 * @param {Store} this
11253 * @param {Object} meta The JSON metadata
11258 * Fires when Records have been added to the Store
11259 * @param {Store} this
11260 * @param {Roo.data.Record[]} records The array of Records added
11261 * @param {Number} index The index at which the record(s) were added
11266 * Fires when a Record has been removed from the Store
11267 * @param {Store} this
11268 * @param {Roo.data.Record} record The Record that was removed
11269 * @param {Number} index The index at which the record was removed
11274 * Fires when a Record has been updated
11275 * @param {Store} this
11276 * @param {Roo.data.Record} record The Record that was updated
11277 * @param {String} operation The update operation being performed. Value may be one of:
11279 Roo.data.Record.EDIT
11280 Roo.data.Record.REJECT
11281 Roo.data.Record.COMMIT
11287 * Fires when the data cache has been cleared.
11288 * @param {Store} this
11292 * @event beforeload
11293 * Fires before a request is made for a new data object. If the beforeload handler returns false
11294 * the load action will be canceled.
11295 * @param {Store} this
11296 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11300 * @event beforeloadadd
11301 * Fires after a new set of Records has been loaded.
11302 * @param {Store} this
11303 * @param {Roo.data.Record[]} records The Records that were loaded
11304 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11306 beforeloadadd : true,
11309 * Fires after a new set of Records has been loaded, before they are added to the store.
11310 * @param {Store} this
11311 * @param {Roo.data.Record[]} records The Records that were loaded
11312 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11313 * @params {Object} return from reader
11317 * @event loadexception
11318 * Fires if an exception occurs in the Proxy during loading.
11319 * Called with the signature of the Proxy's "loadexception" event.
11320 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11323 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11324 * @param {Object} load options
11325 * @param {Object} jsonData from your request (normally this contains the Exception)
11327 loadexception : true
11331 this.proxy = Roo.factory(this.proxy, Roo.data);
11332 this.proxy.xmodule = this.xmodule || false;
11333 this.relayEvents(this.proxy, ["loadexception"]);
11335 this.sortToggle = {};
11336 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11338 Roo.data.Store.superclass.constructor.call(this);
11340 if(this.inlineData){
11341 this.loadData(this.inlineData);
11342 delete this.inlineData;
11346 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11348 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11349 * without a remote query - used by combo/forms at present.
11353 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11356 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11359 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11360 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11363 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11364 * on any HTTP request
11367 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11370 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11374 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11375 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11377 remoteSort : false,
11380 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11381 * loaded or when a record is removed. (defaults to false).
11383 pruneModifiedRecords : false,
11386 lastOptions : null,
11389 * Add Records to the Store and fires the add event.
11390 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11392 add : function(records){
11393 records = [].concat(records);
11394 for(var i = 0, len = records.length; i < len; i++){
11395 records[i].join(this);
11397 var index = this.data.length;
11398 this.data.addAll(records);
11399 this.fireEvent("add", this, records, index);
11403 * Remove a Record from the Store and fires the remove event.
11404 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11406 remove : function(record){
11407 var index = this.data.indexOf(record);
11408 this.data.removeAt(index);
11410 if(this.pruneModifiedRecords){
11411 this.modified.remove(record);
11413 this.fireEvent("remove", this, record, index);
11417 * Remove all Records from the Store and fires the clear event.
11419 removeAll : function(){
11421 if(this.pruneModifiedRecords){
11422 this.modified = [];
11424 this.fireEvent("clear", this);
11428 * Inserts Records to the Store at the given index and fires the add event.
11429 * @param {Number} index The start index at which to insert the passed Records.
11430 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11432 insert : function(index, records){
11433 records = [].concat(records);
11434 for(var i = 0, len = records.length; i < len; i++){
11435 this.data.insert(index, records[i]);
11436 records[i].join(this);
11438 this.fireEvent("add", this, records, index);
11442 * Get the index within the cache of the passed Record.
11443 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11444 * @return {Number} The index of the passed Record. Returns -1 if not found.
11446 indexOf : function(record){
11447 return this.data.indexOf(record);
11451 * Get the index within the cache of the Record with the passed id.
11452 * @param {String} id The id of the Record to find.
11453 * @return {Number} The index of the Record. Returns -1 if not found.
11455 indexOfId : function(id){
11456 return this.data.indexOfKey(id);
11460 * Get the Record with the specified id.
11461 * @param {String} id The id of the Record to find.
11462 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11464 getById : function(id){
11465 return this.data.key(id);
11469 * Get the Record at the specified index.
11470 * @param {Number} index The index of the Record to find.
11471 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11473 getAt : function(index){
11474 return this.data.itemAt(index);
11478 * Returns a range of Records between specified indices.
11479 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11480 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11481 * @return {Roo.data.Record[]} An array of Records
11483 getRange : function(start, end){
11484 return this.data.getRange(start, end);
11488 storeOptions : function(o){
11489 o = Roo.apply({}, o);
11492 this.lastOptions = o;
11496 * Loads the Record cache from the configured Proxy using the configured Reader.
11498 * If using remote paging, then the first load call must specify the <em>start</em>
11499 * and <em>limit</em> properties in the options.params property to establish the initial
11500 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11502 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11503 * and this call will return before the new data has been loaded. Perform any post-processing
11504 * in a callback function, or in a "load" event handler.</strong>
11506 * @param {Object} options An object containing properties which control loading options:<ul>
11507 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11508 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11509 * passed the following arguments:<ul>
11510 * <li>r : Roo.data.Record[]</li>
11511 * <li>options: Options object from the load call</li>
11512 * <li>success: Boolean success indicator</li></ul></li>
11513 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11514 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11517 load : function(options){
11518 options = options || {};
11519 if(this.fireEvent("beforeload", this, options) !== false){
11520 this.storeOptions(options);
11521 var p = Roo.apply(options.params || {}, this.baseParams);
11522 // if meta was not loaded from remote source.. try requesting it.
11523 if (!this.reader.metaFromRemote) {
11524 p._requestMeta = 1;
11526 if(this.sortInfo && this.remoteSort){
11527 var pn = this.paramNames;
11528 p[pn["sort"]] = this.sortInfo.field;
11529 p[pn["dir"]] = this.sortInfo.direction;
11531 if (this.multiSort) {
11532 var pn = this.paramNames;
11533 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11536 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11541 * Reloads the Record cache from the configured Proxy using the configured Reader and
11542 * the options from the last load operation performed.
11543 * @param {Object} options (optional) An object containing properties which may override the options
11544 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11545 * the most recently used options are reused).
11547 reload : function(options){
11548 this.load(Roo.applyIf(options||{}, this.lastOptions));
11552 // Called as a callback by the Reader during a load operation.
11553 loadRecords : function(o, options, success){
11554 if(!o || success === false){
11555 if(success !== false){
11556 this.fireEvent("load", this, [], options, o);
11558 if(options.callback){
11559 options.callback.call(options.scope || this, [], options, false);
11563 // if data returned failure - throw an exception.
11564 if (o.success === false) {
11565 // show a message if no listener is registered.
11566 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11567 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11569 // loadmask wil be hooked into this..
11570 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11573 var r = o.records, t = o.totalRecords || r.length;
11575 this.fireEvent("beforeloadadd", this, r, options, o);
11577 if(!options || options.add !== true){
11578 if(this.pruneModifiedRecords){
11579 this.modified = [];
11581 for(var i = 0, len = r.length; i < len; i++){
11585 this.data = this.snapshot;
11586 delete this.snapshot;
11589 this.data.addAll(r);
11590 this.totalLength = t;
11592 this.fireEvent("datachanged", this);
11594 this.totalLength = Math.max(t, this.data.length+r.length);
11598 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11600 var e = new Roo.data.Record({});
11602 e.set(this.parent.displayField, this.parent.emptyTitle);
11603 e.set(this.parent.valueField, '');
11608 this.fireEvent("load", this, r, options, o);
11609 if(options.callback){
11610 options.callback.call(options.scope || this, r, options, true);
11616 * Loads data from a passed data block. A Reader which understands the format of the data
11617 * must have been configured in the constructor.
11618 * @param {Object} data The data block from which to read the Records. The format of the data expected
11619 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11620 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11622 loadData : function(o, append){
11623 var r = this.reader.readRecords(o);
11624 this.loadRecords(r, {add: append}, true);
11628 * Gets the number of cached records.
11630 * <em>If using paging, this may not be the total size of the dataset. If the data object
11631 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11632 * the data set size</em>
11634 getCount : function(){
11635 return this.data.length || 0;
11639 * Gets the total number of records in the dataset as returned by the server.
11641 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11642 * the dataset size</em>
11644 getTotalCount : function(){
11645 return this.totalLength || 0;
11649 * Returns the sort state of the Store as an object with two properties:
11651 field {String} The name of the field by which the Records are sorted
11652 direction {String} The sort order, "ASC" or "DESC"
11655 getSortState : function(){
11656 return this.sortInfo;
11660 applySort : function(){
11661 if(this.sortInfo && !this.remoteSort){
11662 var s = this.sortInfo, f = s.field;
11663 var st = this.fields.get(f).sortType;
11664 var fn = function(r1, r2){
11665 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11666 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11668 this.data.sort(s.direction, fn);
11669 if(this.snapshot && this.snapshot != this.data){
11670 this.snapshot.sort(s.direction, fn);
11676 * Sets the default sort column and order to be used by the next load operation.
11677 * @param {String} fieldName The name of the field to sort by.
11678 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11680 setDefaultSort : function(field, dir){
11681 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11685 * Sort the Records.
11686 * If remote sorting is used, the sort is performed on the server, and the cache is
11687 * reloaded. If local sorting is used, the cache is sorted internally.
11688 * @param {String} fieldName The name of the field to sort by.
11689 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11691 sort : function(fieldName, dir){
11692 var f = this.fields.get(fieldName);
11694 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11696 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11697 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11702 this.sortToggle[f.name] = dir;
11703 this.sortInfo = {field: f.name, direction: dir};
11704 if(!this.remoteSort){
11706 this.fireEvent("datachanged", this);
11708 this.load(this.lastOptions);
11713 * Calls the specified function for each of the Records in the cache.
11714 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11715 * Returning <em>false</em> aborts and exits the iteration.
11716 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11718 each : function(fn, scope){
11719 this.data.each(fn, scope);
11723 * Gets all records modified since the last commit. Modified records are persisted across load operations
11724 * (e.g., during paging).
11725 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11727 getModifiedRecords : function(){
11728 return this.modified;
11732 createFilterFn : function(property, value, anyMatch){
11733 if(!value.exec){ // not a regex
11734 value = String(value);
11735 if(value.length == 0){
11738 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11740 return function(r){
11741 return value.test(r.data[property]);
11746 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11747 * @param {String} property A field on your records
11748 * @param {Number} start The record index to start at (defaults to 0)
11749 * @param {Number} end The last record index to include (defaults to length - 1)
11750 * @return {Number} The sum
11752 sum : function(property, start, end){
11753 var rs = this.data.items, v = 0;
11754 start = start || 0;
11755 end = (end || end === 0) ? end : rs.length-1;
11757 for(var i = start; i <= end; i++){
11758 v += (rs[i].data[property] || 0);
11764 * Filter the records by a specified property.
11765 * @param {String} field A field on your records
11766 * @param {String/RegExp} value Either a string that the field
11767 * should start with or a RegExp to test against the field
11768 * @param {Boolean} anyMatch True to match any part not just the beginning
11770 filter : function(property, value, anyMatch){
11771 var fn = this.createFilterFn(property, value, anyMatch);
11772 return fn ? this.filterBy(fn) : this.clearFilter();
11776 * Filter by a function. The specified function will be called with each
11777 * record in this data source. If the function returns true the record is included,
11778 * otherwise it is filtered.
11779 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11780 * @param {Object} scope (optional) The scope of the function (defaults to this)
11782 filterBy : function(fn, scope){
11783 this.snapshot = this.snapshot || this.data;
11784 this.data = this.queryBy(fn, scope||this);
11785 this.fireEvent("datachanged", this);
11789 * Query the records by a specified property.
11790 * @param {String} field A field on your records
11791 * @param {String/RegExp} value Either a string that the field
11792 * should start with or a RegExp to test against the field
11793 * @param {Boolean} anyMatch True to match any part not just the beginning
11794 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11796 query : function(property, value, anyMatch){
11797 var fn = this.createFilterFn(property, value, anyMatch);
11798 return fn ? this.queryBy(fn) : this.data.clone();
11802 * Query by a function. The specified function will be called with each
11803 * record in this data source. If the function returns true the record is included
11805 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11806 * @param {Object} scope (optional) The scope of the function (defaults to this)
11807 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11809 queryBy : function(fn, scope){
11810 var data = this.snapshot || this.data;
11811 return data.filterBy(fn, scope||this);
11815 * Collects unique values for a particular dataIndex from this store.
11816 * @param {String} dataIndex The property to collect
11817 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11818 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11819 * @return {Array} An array of the unique values
11821 collect : function(dataIndex, allowNull, bypassFilter){
11822 var d = (bypassFilter === true && this.snapshot) ?
11823 this.snapshot.items : this.data.items;
11824 var v, sv, r = [], l = {};
11825 for(var i = 0, len = d.length; i < len; i++){
11826 v = d[i].data[dataIndex];
11828 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11837 * Revert to a view of the Record cache with no filtering applied.
11838 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11840 clearFilter : function(suppressEvent){
11841 if(this.snapshot && this.snapshot != this.data){
11842 this.data = this.snapshot;
11843 delete this.snapshot;
11844 if(suppressEvent !== true){
11845 this.fireEvent("datachanged", this);
11851 afterEdit : function(record){
11852 if(this.modified.indexOf(record) == -1){
11853 this.modified.push(record);
11855 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11859 afterReject : function(record){
11860 this.modified.remove(record);
11861 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11865 afterCommit : function(record){
11866 this.modified.remove(record);
11867 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11871 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11872 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11874 commitChanges : function(){
11875 var m = this.modified.slice(0);
11876 this.modified = [];
11877 for(var i = 0, len = m.length; i < len; i++){
11883 * Cancel outstanding changes on all changed records.
11885 rejectChanges : function(){
11886 var m = this.modified.slice(0);
11887 this.modified = [];
11888 for(var i = 0, len = m.length; i < len; i++){
11893 onMetaChange : function(meta, rtype, o){
11894 this.recordType = rtype;
11895 this.fields = rtype.prototype.fields;
11896 delete this.snapshot;
11897 this.sortInfo = meta.sortInfo || this.sortInfo;
11898 this.modified = [];
11899 this.fireEvent('metachange', this, this.reader.meta);
11902 moveIndex : function(data, type)
11904 var index = this.indexOf(data);
11906 var newIndex = index + type;
11910 this.insert(newIndex, data);
11915 * Ext JS Library 1.1.1
11916 * Copyright(c) 2006-2007, Ext JS, LLC.
11918 * Originally Released Under LGPL - original licence link has changed is not relivant.
11921 * <script type="text/javascript">
11925 * @class Roo.data.SimpleStore
11926 * @extends Roo.data.Store
11927 * Small helper class to make creating Stores from Array data easier.
11928 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11929 * @cfg {Array} fields An array of field definition objects, or field name strings.
11930 * @cfg {Array} data The multi-dimensional array of data
11932 * @param {Object} config
11934 Roo.data.SimpleStore = function(config){
11935 Roo.data.SimpleStore.superclass.constructor.call(this, {
11937 reader: new Roo.data.ArrayReader({
11940 Roo.data.Record.create(config.fields)
11942 proxy : new Roo.data.MemoryProxy(config.data)
11946 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11948 * Ext JS Library 1.1.1
11949 * Copyright(c) 2006-2007, Ext JS, LLC.
11951 * Originally Released Under LGPL - original licence link has changed is not relivant.
11954 * <script type="text/javascript">
11959 * @extends Roo.data.Store
11960 * @class Roo.data.JsonStore
11961 * Small helper class to make creating Stores for JSON data easier. <br/>
11963 var store = new Roo.data.JsonStore({
11964 url: 'get-images.php',
11966 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11969 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11970 * JsonReader and HttpProxy (unless inline data is provided).</b>
11971 * @cfg {Array} fields An array of field definition objects, or field name strings.
11973 * @param {Object} config
11975 Roo.data.JsonStore = function(c){
11976 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11977 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11978 reader: new Roo.data.JsonReader(c, c.fields)
11981 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11993 Roo.data.Field = function(config){
11994 if(typeof config == "string"){
11995 config = {name: config};
11997 Roo.apply(this, config);
12000 this.type = "auto";
12003 var st = Roo.data.SortTypes;
12004 // named sortTypes are supported, here we look them up
12005 if(typeof this.sortType == "string"){
12006 this.sortType = st[this.sortType];
12009 // set default sortType for strings and dates
12010 if(!this.sortType){
12013 this.sortType = st.asUCString;
12016 this.sortType = st.asDate;
12019 this.sortType = st.none;
12024 var stripRe = /[\$,%]/g;
12026 // prebuilt conversion function for this field, instead of
12027 // switching every time we're reading a value
12029 var cv, dateFormat = this.dateFormat;
12034 cv = function(v){ return v; };
12037 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12041 return v !== undefined && v !== null && v !== '' ?
12042 parseInt(String(v).replace(stripRe, ""), 10) : '';
12047 return v !== undefined && v !== null && v !== '' ?
12048 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12053 cv = function(v){ return v === true || v === "true" || v == 1; };
12060 if(v instanceof Date){
12064 if(dateFormat == "timestamp"){
12065 return new Date(v*1000);
12067 return Date.parseDate(v, dateFormat);
12069 var parsed = Date.parse(v);
12070 return parsed ? new Date(parsed) : null;
12079 Roo.data.Field.prototype = {
12087 * Ext JS Library 1.1.1
12088 * Copyright(c) 2006-2007, Ext JS, LLC.
12090 * Originally Released Under LGPL - original licence link has changed is not relivant.
12093 * <script type="text/javascript">
12096 // Base class for reading structured data from a data source. This class is intended to be
12097 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12100 * @class Roo.data.DataReader
12101 * Base class for reading structured data from a data source. This class is intended to be
12102 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12105 Roo.data.DataReader = function(meta, recordType){
12109 this.recordType = recordType instanceof Array ?
12110 Roo.data.Record.create(recordType) : recordType;
12113 Roo.data.DataReader.prototype = {
12115 * Create an empty record
12116 * @param {Object} data (optional) - overlay some values
12117 * @return {Roo.data.Record} record created.
12119 newRow : function(d) {
12121 this.recordType.prototype.fields.each(function(c) {
12123 case 'int' : da[c.name] = 0; break;
12124 case 'date' : da[c.name] = new Date(); break;
12125 case 'float' : da[c.name] = 0.0; break;
12126 case 'boolean' : da[c.name] = false; break;
12127 default : da[c.name] = ""; break;
12131 return new this.recordType(Roo.apply(da, d));
12136 * Ext JS Library 1.1.1
12137 * Copyright(c) 2006-2007, Ext JS, LLC.
12139 * Originally Released Under LGPL - original licence link has changed is not relivant.
12142 * <script type="text/javascript">
12146 * @class Roo.data.DataProxy
12147 * @extends Roo.data.Observable
12148 * This class is an abstract base class for implementations which provide retrieval of
12149 * unformatted data objects.<br>
12151 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12152 * (of the appropriate type which knows how to parse the data object) to provide a block of
12153 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12155 * Custom implementations must implement the load method as described in
12156 * {@link Roo.data.HttpProxy#load}.
12158 Roo.data.DataProxy = function(){
12161 * @event beforeload
12162 * Fires before a network request is made to retrieve a data object.
12163 * @param {Object} This DataProxy object.
12164 * @param {Object} params The params parameter to the load function.
12169 * Fires before the load method's callback is called.
12170 * @param {Object} This DataProxy object.
12171 * @param {Object} o The data object.
12172 * @param {Object} arg The callback argument object passed to the load function.
12176 * @event loadexception
12177 * Fires if an Exception occurs during data retrieval.
12178 * @param {Object} This DataProxy object.
12179 * @param {Object} o The data object.
12180 * @param {Object} arg The callback argument object passed to the load function.
12181 * @param {Object} e The Exception.
12183 loadexception : true
12185 Roo.data.DataProxy.superclass.constructor.call(this);
12188 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12191 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12195 * Ext JS Library 1.1.1
12196 * Copyright(c) 2006-2007, Ext JS, LLC.
12198 * Originally Released Under LGPL - original licence link has changed is not relivant.
12201 * <script type="text/javascript">
12204 * @class Roo.data.MemoryProxy
12205 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12206 * to the Reader when its load method is called.
12208 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12210 Roo.data.MemoryProxy = function(data){
12214 Roo.data.MemoryProxy.superclass.constructor.call(this);
12218 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12221 * Load data from the requested source (in this case an in-memory
12222 * data object passed to the constructor), read the data object into
12223 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12224 * process that block using the passed callback.
12225 * @param {Object} params This parameter is not used by the MemoryProxy class.
12226 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12227 * object into a block of Roo.data.Records.
12228 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12229 * The function must be passed <ul>
12230 * <li>The Record block object</li>
12231 * <li>The "arg" argument from the load function</li>
12232 * <li>A boolean success indicator</li>
12234 * @param {Object} scope The scope in which to call the callback
12235 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12237 load : function(params, reader, callback, scope, arg){
12238 params = params || {};
12241 result = reader.readRecords(this.data);
12243 this.fireEvent("loadexception", this, arg, null, e);
12244 callback.call(scope, null, arg, false);
12247 callback.call(scope, result, arg, true);
12251 update : function(params, records){
12256 * Ext JS Library 1.1.1
12257 * Copyright(c) 2006-2007, Ext JS, LLC.
12259 * Originally Released Under LGPL - original licence link has changed is not relivant.
12262 * <script type="text/javascript">
12265 * @class Roo.data.HttpProxy
12266 * @extends Roo.data.DataProxy
12267 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12268 * configured to reference a certain URL.<br><br>
12270 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12271 * from which the running page was served.<br><br>
12273 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12275 * Be aware that to enable the browser to parse an XML document, the server must set
12276 * the Content-Type header in the HTTP response to "text/xml".
12278 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12279 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12280 * will be used to make the request.
12282 Roo.data.HttpProxy = function(conn){
12283 Roo.data.HttpProxy.superclass.constructor.call(this);
12284 // is conn a conn config or a real conn?
12286 this.useAjax = !conn || !conn.events;
12290 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12291 // thse are take from connection...
12294 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12297 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12298 * extra parameters to each request made by this object. (defaults to undefined)
12301 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12302 * to each request made by this object. (defaults to undefined)
12305 * @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)
12308 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12311 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12317 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12321 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12322 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12323 * a finer-grained basis than the DataProxy events.
12325 getConnection : function(){
12326 return this.useAjax ? Roo.Ajax : this.conn;
12330 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12331 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12332 * process that block using the passed callback.
12333 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12334 * for the request to the remote server.
12335 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12336 * object into a block of Roo.data.Records.
12337 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12338 * The function must be passed <ul>
12339 * <li>The Record block object</li>
12340 * <li>The "arg" argument from the load function</li>
12341 * <li>A boolean success indicator</li>
12343 * @param {Object} scope The scope in which to call the callback
12344 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12346 load : function(params, reader, callback, scope, arg){
12347 if(this.fireEvent("beforeload", this, params) !== false){
12349 params : params || {},
12351 callback : callback,
12356 callback : this.loadResponse,
12360 Roo.applyIf(o, this.conn);
12361 if(this.activeRequest){
12362 Roo.Ajax.abort(this.activeRequest);
12364 this.activeRequest = Roo.Ajax.request(o);
12366 this.conn.request(o);
12369 callback.call(scope||this, null, arg, false);
12374 loadResponse : function(o, success, response){
12375 delete this.activeRequest;
12377 this.fireEvent("loadexception", this, o, response);
12378 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12383 result = o.reader.read(response);
12385 this.fireEvent("loadexception", this, o, response, e);
12386 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12390 this.fireEvent("load", this, o, o.request.arg);
12391 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12395 update : function(dataSet){
12400 updateResponse : function(dataSet){
12405 * Ext JS Library 1.1.1
12406 * Copyright(c) 2006-2007, Ext JS, LLC.
12408 * Originally Released Under LGPL - original licence link has changed is not relivant.
12411 * <script type="text/javascript">
12415 * @class Roo.data.ScriptTagProxy
12416 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12417 * other than the originating domain of the running page.<br><br>
12419 * <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
12420 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12422 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12423 * source code that is used as the source inside a <script> tag.<br><br>
12425 * In order for the browser to process the returned data, the server must wrap the data object
12426 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12427 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12428 * depending on whether the callback name was passed:
12431 boolean scriptTag = false;
12432 String cb = request.getParameter("callback");
12435 response.setContentType("text/javascript");
12437 response.setContentType("application/x-json");
12439 Writer out = response.getWriter();
12441 out.write(cb + "(");
12443 out.print(dataBlock.toJsonString());
12450 * @param {Object} config A configuration object.
12452 Roo.data.ScriptTagProxy = function(config){
12453 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12454 Roo.apply(this, config);
12455 this.head = document.getElementsByTagName("head")[0];
12458 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12460 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12462 * @cfg {String} url The URL from which to request the data object.
12465 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12469 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12470 * the server the name of the callback function set up by the load call to process the returned data object.
12471 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12472 * javascript output which calls this named function passing the data object as its only parameter.
12474 callbackParam : "callback",
12476 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12477 * name to the request.
12482 * Load data from the configured URL, read the data object into
12483 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12484 * process that block using the passed callback.
12485 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12486 * for the request to the remote server.
12487 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12488 * object into a block of Roo.data.Records.
12489 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12490 * The function must be passed <ul>
12491 * <li>The Record block object</li>
12492 * <li>The "arg" argument from the load function</li>
12493 * <li>A boolean success indicator</li>
12495 * @param {Object} scope The scope in which to call the callback
12496 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12498 load : function(params, reader, callback, scope, arg){
12499 if(this.fireEvent("beforeload", this, params) !== false){
12501 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12503 var url = this.url;
12504 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12506 url += "&_dc=" + (new Date().getTime());
12508 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12511 cb : "stcCallback"+transId,
12512 scriptId : "stcScript"+transId,
12516 callback : callback,
12522 window[trans.cb] = function(o){
12523 conn.handleResponse(o, trans);
12526 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12528 if(this.autoAbort !== false){
12532 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12534 var script = document.createElement("script");
12535 script.setAttribute("src", url);
12536 script.setAttribute("type", "text/javascript");
12537 script.setAttribute("id", trans.scriptId);
12538 this.head.appendChild(script);
12540 this.trans = trans;
12542 callback.call(scope||this, null, arg, false);
12547 isLoading : function(){
12548 return this.trans ? true : false;
12552 * Abort the current server request.
12554 abort : function(){
12555 if(this.isLoading()){
12556 this.destroyTrans(this.trans);
12561 destroyTrans : function(trans, isLoaded){
12562 this.head.removeChild(document.getElementById(trans.scriptId));
12563 clearTimeout(trans.timeoutId);
12565 window[trans.cb] = undefined;
12567 delete window[trans.cb];
12570 // if hasn't been loaded, wait for load to remove it to prevent script error
12571 window[trans.cb] = function(){
12572 window[trans.cb] = undefined;
12574 delete window[trans.cb];
12581 handleResponse : function(o, trans){
12582 this.trans = false;
12583 this.destroyTrans(trans, true);
12586 result = trans.reader.readRecords(o);
12588 this.fireEvent("loadexception", this, o, trans.arg, e);
12589 trans.callback.call(trans.scope||window, null, trans.arg, false);
12592 this.fireEvent("load", this, o, trans.arg);
12593 trans.callback.call(trans.scope||window, result, trans.arg, true);
12597 handleFailure : function(trans){
12598 this.trans = false;
12599 this.destroyTrans(trans, false);
12600 this.fireEvent("loadexception", this, null, trans.arg);
12601 trans.callback.call(trans.scope||window, null, trans.arg, false);
12605 * Ext JS Library 1.1.1
12606 * Copyright(c) 2006-2007, Ext JS, LLC.
12608 * Originally Released Under LGPL - original licence link has changed is not relivant.
12611 * <script type="text/javascript">
12615 * @class Roo.data.JsonReader
12616 * @extends Roo.data.DataReader
12617 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12618 * based on mappings in a provided Roo.data.Record constructor.
12620 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12621 * in the reply previously.
12626 var RecordDef = Roo.data.Record.create([
12627 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12628 {name: 'occupation'} // This field will use "occupation" as the mapping.
12630 var myReader = new Roo.data.JsonReader({
12631 totalProperty: "results", // The property which contains the total dataset size (optional)
12632 root: "rows", // The property which contains an Array of row objects
12633 id: "id" // The property within each row object that provides an ID for the record (optional)
12637 * This would consume a JSON file like this:
12639 { 'results': 2, 'rows': [
12640 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12641 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12644 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12645 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12646 * paged from the remote server.
12647 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12648 * @cfg {String} root name of the property which contains the Array of row objects.
12649 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12650 * @cfg {Array} fields Array of field definition objects
12652 * Create a new JsonReader
12653 * @param {Object} meta Metadata configuration options
12654 * @param {Object} recordType Either an Array of field definition objects,
12655 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12657 Roo.data.JsonReader = function(meta, recordType){
12660 // set some defaults:
12661 Roo.applyIf(meta, {
12662 totalProperty: 'total',
12663 successProperty : 'success',
12668 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12670 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12673 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12674 * Used by Store query builder to append _requestMeta to params.
12677 metaFromRemote : false,
12679 * This method is only used by a DataProxy which has retrieved data from a remote server.
12680 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12681 * @return {Object} data A data block which is used by an Roo.data.Store object as
12682 * a cache of Roo.data.Records.
12684 read : function(response){
12685 var json = response.responseText;
12687 var o = /* eval:var:o */ eval("("+json+")");
12689 throw {message: "JsonReader.read: Json object not found"};
12695 this.metaFromRemote = true;
12696 this.meta = o.metaData;
12697 this.recordType = Roo.data.Record.create(o.metaData.fields);
12698 this.onMetaChange(this.meta, this.recordType, o);
12700 return this.readRecords(o);
12703 // private function a store will implement
12704 onMetaChange : function(meta, recordType, o){
12711 simpleAccess: function(obj, subsc) {
12718 getJsonAccessor: function(){
12720 return function(expr) {
12722 return(re.test(expr))
12723 ? new Function("obj", "return obj." + expr)
12728 return Roo.emptyFn;
12733 * Create a data block containing Roo.data.Records from an XML document.
12734 * @param {Object} o An object which contains an Array of row objects in the property specified
12735 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12736 * which contains the total size of the dataset.
12737 * @return {Object} data A data block which is used by an Roo.data.Store object as
12738 * a cache of Roo.data.Records.
12740 readRecords : function(o){
12742 * After any data loads, the raw JSON data is available for further custom processing.
12746 var s = this.meta, Record = this.recordType,
12747 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12749 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12751 if(s.totalProperty) {
12752 this.getTotal = this.getJsonAccessor(s.totalProperty);
12754 if(s.successProperty) {
12755 this.getSuccess = this.getJsonAccessor(s.successProperty);
12757 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12759 var g = this.getJsonAccessor(s.id);
12760 this.getId = function(rec) {
12762 return (r === undefined || r === "") ? null : r;
12765 this.getId = function(){return null;};
12768 for(var jj = 0; jj < fl; jj++){
12770 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12771 this.ef[jj] = this.getJsonAccessor(map);
12775 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12776 if(s.totalProperty){
12777 var vt = parseInt(this.getTotal(o), 10);
12782 if(s.successProperty){
12783 var vs = this.getSuccess(o);
12784 if(vs === false || vs === 'false'){
12789 for(var i = 0; i < c; i++){
12792 var id = this.getId(n);
12793 for(var j = 0; j < fl; j++){
12795 var v = this.ef[j](n);
12797 Roo.log('missing convert for ' + f.name);
12801 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12803 var record = new Record(values, id);
12805 records[i] = record;
12811 totalRecords : totalRecords
12816 * Ext JS Library 1.1.1
12817 * Copyright(c) 2006-2007, Ext JS, LLC.
12819 * Originally Released Under LGPL - original licence link has changed is not relivant.
12822 * <script type="text/javascript">
12826 * @class Roo.data.ArrayReader
12827 * @extends Roo.data.DataReader
12828 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12829 * Each element of that Array represents a row of data fields. The
12830 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12831 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12835 var RecordDef = Roo.data.Record.create([
12836 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12837 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12839 var myReader = new Roo.data.ArrayReader({
12840 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12844 * This would consume an Array like this:
12846 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12848 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12850 * Create a new JsonReader
12851 * @param {Object} meta Metadata configuration options.
12852 * @param {Object} recordType Either an Array of field definition objects
12853 * as specified to {@link Roo.data.Record#create},
12854 * or an {@link Roo.data.Record} object
12855 * created using {@link Roo.data.Record#create}.
12857 Roo.data.ArrayReader = function(meta, recordType){
12858 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12861 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12863 * Create a data block containing Roo.data.Records from an XML document.
12864 * @param {Object} o An Array of row objects which represents the dataset.
12865 * @return {Object} data A data block which is used by an Roo.data.Store object as
12866 * a cache of Roo.data.Records.
12868 readRecords : function(o){
12869 var sid = this.meta ? this.meta.id : null;
12870 var recordType = this.recordType, fields = recordType.prototype.fields;
12873 for(var i = 0; i < root.length; i++){
12876 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12877 for(var j = 0, jlen = fields.length; j < jlen; j++){
12878 var f = fields.items[j];
12879 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12880 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12882 values[f.name] = v;
12884 var record = new recordType(values, id);
12886 records[records.length] = record;
12890 totalRecords : records.length
12899 * @class Roo.bootstrap.ComboBox
12900 * @extends Roo.bootstrap.TriggerField
12901 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12902 * @cfg {Boolean} append (true|false) default false
12903 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12904 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12905 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12906 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12907 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12908 * @cfg {Boolean} animate default true
12909 * @cfg {Boolean} emptyResultText only for touch device
12910 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12911 * @cfg {String} emptyTitle default ''
12913 * Create a new ComboBox.
12914 * @param {Object} config Configuration options
12916 Roo.bootstrap.ComboBox = function(config){
12917 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12921 * Fires when the dropdown list is expanded
12922 * @param {Roo.bootstrap.ComboBox} combo This combo box
12927 * Fires when the dropdown list is collapsed
12928 * @param {Roo.bootstrap.ComboBox} combo This combo box
12932 * @event beforeselect
12933 * Fires before a list item is selected. Return false to cancel the selection.
12934 * @param {Roo.bootstrap.ComboBox} combo This combo box
12935 * @param {Roo.data.Record} record The data record returned from the underlying store
12936 * @param {Number} index The index of the selected item in the dropdown list
12938 'beforeselect' : true,
12941 * Fires when a list item is selected
12942 * @param {Roo.bootstrap.ComboBox} combo This combo box
12943 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12944 * @param {Number} index The index of the selected item in the dropdown list
12948 * @event beforequery
12949 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12950 * The event object passed has these properties:
12951 * @param {Roo.bootstrap.ComboBox} combo This combo box
12952 * @param {String} query The query
12953 * @param {Boolean} forceAll true to force "all" query
12954 * @param {Boolean} cancel true to cancel the query
12955 * @param {Object} e The query event object
12957 'beforequery': true,
12960 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12961 * @param {Roo.bootstrap.ComboBox} combo This combo box
12966 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12967 * @param {Roo.bootstrap.ComboBox} combo This combo box
12968 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12973 * Fires when the remove value from the combobox array
12974 * @param {Roo.bootstrap.ComboBox} combo This combo box
12978 * @event afterremove
12979 * Fires when the remove value from the combobox array
12980 * @param {Roo.bootstrap.ComboBox} combo This combo box
12982 'afterremove' : true,
12984 * @event specialfilter
12985 * Fires when specialfilter
12986 * @param {Roo.bootstrap.ComboBox} combo This combo box
12988 'specialfilter' : true,
12991 * Fires when tick the element
12992 * @param {Roo.bootstrap.ComboBox} combo This combo box
12996 * @event touchviewdisplay
12997 * Fires when touch view require special display (default is using displayField)
12998 * @param {Roo.bootstrap.ComboBox} combo This combo box
12999 * @param {Object} cfg set html .
13001 'touchviewdisplay' : true
13006 this.tickItems = [];
13008 this.selectedIndex = -1;
13009 if(this.mode == 'local'){
13010 if(config.queryDelay === undefined){
13011 this.queryDelay = 10;
13013 if(config.minChars === undefined){
13019 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13022 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13023 * rendering into an Roo.Editor, defaults to false)
13026 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13027 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13030 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13033 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13034 * the dropdown list (defaults to undefined, with no header element)
13038 * @cfg {String/Roo.Template} tpl The template to use to render the output
13042 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13044 listWidth: undefined,
13046 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13047 * mode = 'remote' or 'text' if mode = 'local')
13049 displayField: undefined,
13052 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13053 * mode = 'remote' or 'value' if mode = 'local').
13054 * Note: use of a valueField requires the user make a selection
13055 * in order for a value to be mapped.
13057 valueField: undefined,
13059 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13064 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13065 * field's data value (defaults to the underlying DOM element's name)
13067 hiddenName: undefined,
13069 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13073 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13075 selectedClass: 'active',
13078 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13082 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13083 * anchor positions (defaults to 'tl-bl')
13085 listAlign: 'tl-bl?',
13087 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13091 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13092 * query specified by the allQuery config option (defaults to 'query')
13094 triggerAction: 'query',
13096 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13097 * (defaults to 4, does not apply if editable = false)
13101 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13102 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13106 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13107 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13111 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13112 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13116 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13117 * when editable = true (defaults to false)
13119 selectOnFocus:false,
13121 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13123 queryParam: 'query',
13125 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13126 * when mode = 'remote' (defaults to 'Loading...')
13128 loadingText: 'Loading...',
13130 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13134 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13138 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13139 * traditional select (defaults to true)
13143 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13147 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13151 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13152 * listWidth has a higher value)
13156 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13157 * allow the user to set arbitrary text into the field (defaults to false)
13159 forceSelection:false,
13161 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13162 * if typeAhead = true (defaults to 250)
13164 typeAheadDelay : 250,
13166 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13167 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13169 valueNotFoundText : undefined,
13171 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13173 blockFocus : false,
13176 * @cfg {Boolean} disableClear Disable showing of clear button.
13178 disableClear : false,
13180 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13182 alwaysQuery : false,
13185 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13190 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13192 invalidClass : "has-warning",
13195 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13197 validClass : "has-success",
13200 * @cfg {Boolean} specialFilter (true|false) special filter default false
13202 specialFilter : false,
13205 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13207 mobileTouchView : true,
13210 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13212 useNativeIOS : false,
13215 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13217 mobile_restrict_height : false,
13219 ios_options : false,
13231 btnPosition : 'right',
13232 triggerList : true,
13233 showToggleBtn : true,
13235 emptyResultText: 'Empty',
13236 triggerText : 'Select',
13239 // element that contains real text value.. (when hidden is used..)
13241 getAutoCreate : function()
13246 * Render classic select for iso
13249 if(Roo.isIOS && this.useNativeIOS){
13250 cfg = this.getAutoCreateNativeIOS();
13258 if(Roo.isTouch && this.mobileTouchView){
13259 cfg = this.getAutoCreateTouchView();
13266 if(!this.tickable){
13267 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13272 * ComboBox with tickable selections
13275 var align = this.labelAlign || this.parentLabelAlign();
13278 cls : 'form-group roo-combobox-tickable' //input-group
13281 var btn_text_select = '';
13282 var btn_text_done = '';
13283 var btn_text_cancel = '';
13285 if (this.btn_text_show) {
13286 btn_text_select = 'Select';
13287 btn_text_done = 'Done';
13288 btn_text_cancel = 'Cancel';
13293 cls : 'tickable-buttons',
13298 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13299 //html : this.triggerText
13300 html: btn_text_select
13306 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13308 html: btn_text_done
13314 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13316 html: btn_text_cancel
13322 buttons.cn.unshift({
13324 cls: 'roo-select2-search-field-input'
13330 Roo.each(buttons.cn, function(c){
13332 c.cls += ' btn-' + _this.size;
13335 if (_this.disabled) {
13346 cls: 'form-hidden-field'
13350 cls: 'roo-select2-choices',
13354 cls: 'roo-select2-search-field',
13365 cls: 'roo-select2-container input-group roo-select2-container-multi',
13371 // cls: 'typeahead typeahead-long dropdown-menu',
13372 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13377 if(this.hasFeedback && !this.allowBlank){
13381 cls: 'glyphicon form-control-feedback'
13384 combobox.cn.push(feedback);
13389 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13390 tooltip : 'This field is required'
13392 if (Roo.bootstrap.version == 4) {
13395 style : 'display:none'
13398 if (align ==='left' && this.fieldLabel.length) {
13400 cfg.cls += ' roo-form-group-label-left row';
13407 cls : 'control-label col-form-label',
13408 html : this.fieldLabel
13420 var labelCfg = cfg.cn[1];
13421 var contentCfg = cfg.cn[2];
13424 if(this.indicatorpos == 'right'){
13430 cls : 'control-label col-form-label',
13434 html : this.fieldLabel
13450 labelCfg = cfg.cn[0];
13451 contentCfg = cfg.cn[1];
13455 if(this.labelWidth > 12){
13456 labelCfg.style = "width: " + this.labelWidth + 'px';
13459 if(this.labelWidth < 13 && this.labelmd == 0){
13460 this.labelmd = this.labelWidth;
13463 if(this.labellg > 0){
13464 labelCfg.cls += ' col-lg-' + this.labellg;
13465 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13468 if(this.labelmd > 0){
13469 labelCfg.cls += ' col-md-' + this.labelmd;
13470 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13473 if(this.labelsm > 0){
13474 labelCfg.cls += ' col-sm-' + this.labelsm;
13475 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13478 if(this.labelxs > 0){
13479 labelCfg.cls += ' col-xs-' + this.labelxs;
13480 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13484 } else if ( this.fieldLabel.length) {
13485 // Roo.log(" label");
13490 //cls : 'input-group-addon',
13491 html : this.fieldLabel
13496 if(this.indicatorpos == 'right'){
13500 //cls : 'input-group-addon',
13501 html : this.fieldLabel
13511 // Roo.log(" no label && no align");
13518 ['xs','sm','md','lg'].map(function(size){
13519 if (settings[size]) {
13520 cfg.cls += ' col-' + size + '-' + settings[size];
13528 _initEventsCalled : false,
13531 initEvents: function()
13533 if (this._initEventsCalled) { // as we call render... prevent looping...
13536 this._initEventsCalled = true;
13539 throw "can not find store for combo";
13542 this.indicator = this.indicatorEl();
13544 this.store = Roo.factory(this.store, Roo.data);
13545 this.store.parent = this;
13547 // if we are building from html. then this element is so complex, that we can not really
13548 // use the rendered HTML.
13549 // so we have to trash and replace the previous code.
13550 if (Roo.XComponent.build_from_html) {
13551 // remove this element....
13552 var e = this.el.dom, k=0;
13553 while (e ) { e = e.previousSibling; ++k;}
13558 this.rendered = false;
13560 this.render(this.parent().getChildContainer(true), k);
13563 if(Roo.isIOS && this.useNativeIOS){
13564 this.initIOSView();
13572 if(Roo.isTouch && this.mobileTouchView){
13573 this.initTouchView();
13578 this.initTickableEvents();
13582 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13584 if(this.hiddenName){
13586 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13588 this.hiddenField.dom.value =
13589 this.hiddenValue !== undefined ? this.hiddenValue :
13590 this.value !== undefined ? this.value : '';
13592 // prevent input submission
13593 this.el.dom.removeAttribute('name');
13594 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13599 // this.el.dom.setAttribute('autocomplete', 'off');
13602 var cls = 'x-combo-list';
13604 //this.list = new Roo.Layer({
13605 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13611 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13612 _this.list.setWidth(lw);
13615 this.list.on('mouseover', this.onViewOver, this);
13616 this.list.on('mousemove', this.onViewMove, this);
13617 this.list.on('scroll', this.onViewScroll, this);
13620 this.list.swallowEvent('mousewheel');
13621 this.assetHeight = 0;
13624 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13625 this.assetHeight += this.header.getHeight();
13628 this.innerList = this.list.createChild({cls:cls+'-inner'});
13629 this.innerList.on('mouseover', this.onViewOver, this);
13630 this.innerList.on('mousemove', this.onViewMove, this);
13631 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13633 if(this.allowBlank && !this.pageSize && !this.disableClear){
13634 this.footer = this.list.createChild({cls:cls+'-ft'});
13635 this.pageTb = new Roo.Toolbar(this.footer);
13639 this.footer = this.list.createChild({cls:cls+'-ft'});
13640 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13641 {pageSize: this.pageSize});
13645 if (this.pageTb && this.allowBlank && !this.disableClear) {
13647 this.pageTb.add(new Roo.Toolbar.Fill(), {
13648 cls: 'x-btn-icon x-btn-clear',
13650 handler: function()
13653 _this.clearValue();
13654 _this.onSelect(false, -1);
13659 this.assetHeight += this.footer.getHeight();
13664 this.tpl = Roo.bootstrap.version == 4 ?
13665 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13666 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13669 this.view = new Roo.View(this.list, this.tpl, {
13670 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13672 //this.view.wrapEl.setDisplayed(false);
13673 this.view.on('click', this.onViewClick, this);
13676 this.store.on('beforeload', this.onBeforeLoad, this);
13677 this.store.on('load', this.onLoad, this);
13678 this.store.on('loadexception', this.onLoadException, this);
13680 if(this.resizable){
13681 this.resizer = new Roo.Resizable(this.list, {
13682 pinned:true, handles:'se'
13684 this.resizer.on('resize', function(r, w, h){
13685 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13686 this.listWidth = w;
13687 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13688 this.restrictHeight();
13690 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13693 if(!this.editable){
13694 this.editable = true;
13695 this.setEditable(false);
13700 if (typeof(this.events.add.listeners) != 'undefined') {
13702 this.addicon = this.wrap.createChild(
13703 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13705 this.addicon.on('click', function(e) {
13706 this.fireEvent('add', this);
13709 if (typeof(this.events.edit.listeners) != 'undefined') {
13711 this.editicon = this.wrap.createChild(
13712 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13713 if (this.addicon) {
13714 this.editicon.setStyle('margin-left', '40px');
13716 this.editicon.on('click', function(e) {
13718 // we fire even if inothing is selected..
13719 this.fireEvent('edit', this, this.lastData );
13725 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13726 "up" : function(e){
13727 this.inKeyMode = true;
13731 "down" : function(e){
13732 if(!this.isExpanded()){
13733 this.onTriggerClick();
13735 this.inKeyMode = true;
13740 "enter" : function(e){
13741 // this.onViewClick();
13745 if(this.fireEvent("specialkey", this, e)){
13746 this.onViewClick(false);
13752 "esc" : function(e){
13756 "tab" : function(e){
13759 if(this.fireEvent("specialkey", this, e)){
13760 this.onViewClick(false);
13768 doRelay : function(foo, bar, hname){
13769 if(hname == 'down' || this.scope.isExpanded()){
13770 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13779 this.queryDelay = Math.max(this.queryDelay || 10,
13780 this.mode == 'local' ? 10 : 250);
13783 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13785 if(this.typeAhead){
13786 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13788 if(this.editable !== false){
13789 this.inputEl().on("keyup", this.onKeyUp, this);
13791 if(this.forceSelection){
13792 this.inputEl().on('blur', this.doForce, this);
13796 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13797 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13801 initTickableEvents: function()
13805 if(this.hiddenName){
13807 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13809 this.hiddenField.dom.value =
13810 this.hiddenValue !== undefined ? this.hiddenValue :
13811 this.value !== undefined ? this.value : '';
13813 // prevent input submission
13814 this.el.dom.removeAttribute('name');
13815 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13820 // this.list = this.el.select('ul.dropdown-menu',true).first();
13822 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13823 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13824 if(this.triggerList){
13825 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13828 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13829 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13831 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13832 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13834 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13835 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13837 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13838 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13839 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13842 this.cancelBtn.hide();
13847 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13848 _this.list.setWidth(lw);
13851 this.list.on('mouseover', this.onViewOver, this);
13852 this.list.on('mousemove', this.onViewMove, this);
13854 this.list.on('scroll', this.onViewScroll, this);
13857 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13858 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13861 this.view = new Roo.View(this.list, this.tpl, {
13866 selectedClass: this.selectedClass
13869 //this.view.wrapEl.setDisplayed(false);
13870 this.view.on('click', this.onViewClick, this);
13874 this.store.on('beforeload', this.onBeforeLoad, this);
13875 this.store.on('load', this.onLoad, this);
13876 this.store.on('loadexception', this.onLoadException, this);
13879 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13880 "up" : function(e){
13881 this.inKeyMode = true;
13885 "down" : function(e){
13886 this.inKeyMode = true;
13890 "enter" : function(e){
13891 if(this.fireEvent("specialkey", this, e)){
13892 this.onViewClick(false);
13898 "esc" : function(e){
13899 this.onTickableFooterButtonClick(e, false, false);
13902 "tab" : function(e){
13903 this.fireEvent("specialkey", this, e);
13905 this.onTickableFooterButtonClick(e, false, false);
13912 doRelay : function(e, fn, key){
13913 if(this.scope.isExpanded()){
13914 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13923 this.queryDelay = Math.max(this.queryDelay || 10,
13924 this.mode == 'local' ? 10 : 250);
13927 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13929 if(this.typeAhead){
13930 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13933 if(this.editable !== false){
13934 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13937 this.indicator = this.indicatorEl();
13939 if(this.indicator){
13940 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13941 this.indicator.hide();
13946 onDestroy : function(){
13948 this.view.setStore(null);
13949 this.view.el.removeAllListeners();
13950 this.view.el.remove();
13951 this.view.purgeListeners();
13954 this.list.dom.innerHTML = '';
13958 this.store.un('beforeload', this.onBeforeLoad, this);
13959 this.store.un('load', this.onLoad, this);
13960 this.store.un('loadexception', this.onLoadException, this);
13962 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13966 fireKey : function(e){
13967 if(e.isNavKeyPress() && !this.list.isVisible()){
13968 this.fireEvent("specialkey", this, e);
13973 onResize: function(w, h){
13974 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13976 // if(typeof w != 'number'){
13977 // // we do not handle it!?!?
13980 // var tw = this.trigger.getWidth();
13981 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13982 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13984 // this.inputEl().setWidth( this.adjustWidth('input', x));
13986 // //this.trigger.setStyle('left', x+'px');
13988 // if(this.list && this.listWidth === undefined){
13989 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13990 // this.list.setWidth(lw);
13991 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13999 * Allow or prevent the user from directly editing the field text. If false is passed,
14000 * the user will only be able to select from the items defined in the dropdown list. This method
14001 * is the runtime equivalent of setting the 'editable' config option at config time.
14002 * @param {Boolean} value True to allow the user to directly edit the field text
14004 setEditable : function(value){
14005 if(value == this.editable){
14008 this.editable = value;
14010 this.inputEl().dom.setAttribute('readOnly', true);
14011 this.inputEl().on('mousedown', this.onTriggerClick, this);
14012 this.inputEl().addClass('x-combo-noedit');
14014 this.inputEl().dom.setAttribute('readOnly', false);
14015 this.inputEl().un('mousedown', this.onTriggerClick, this);
14016 this.inputEl().removeClass('x-combo-noedit');
14022 onBeforeLoad : function(combo,opts){
14023 if(!this.hasFocus){
14027 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14029 this.restrictHeight();
14030 this.selectedIndex = -1;
14034 onLoad : function(){
14036 this.hasQuery = false;
14038 if(!this.hasFocus){
14042 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14043 this.loading.hide();
14046 if(this.store.getCount() > 0){
14049 this.restrictHeight();
14050 if(this.lastQuery == this.allQuery){
14051 if(this.editable && !this.tickable){
14052 this.inputEl().dom.select();
14056 !this.selectByValue(this.value, true) &&
14059 !this.store.lastOptions ||
14060 typeof(this.store.lastOptions.add) == 'undefined' ||
14061 this.store.lastOptions.add != true
14064 this.select(0, true);
14067 if(this.autoFocus){
14070 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14071 this.taTask.delay(this.typeAheadDelay);
14075 this.onEmptyResults();
14081 onLoadException : function()
14083 this.hasQuery = false;
14085 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14086 this.loading.hide();
14089 if(this.tickable && this.editable){
14094 // only causes errors at present
14095 //Roo.log(this.store.reader.jsonData);
14096 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14098 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14104 onTypeAhead : function(){
14105 if(this.store.getCount() > 0){
14106 var r = this.store.getAt(0);
14107 var newValue = r.data[this.displayField];
14108 var len = newValue.length;
14109 var selStart = this.getRawValue().length;
14111 if(selStart != len){
14112 this.setRawValue(newValue);
14113 this.selectText(selStart, newValue.length);
14119 onSelect : function(record, index){
14121 if(this.fireEvent('beforeselect', this, record, index) !== false){
14123 this.setFromData(index > -1 ? record.data : false);
14126 this.fireEvent('select', this, record, index);
14131 * Returns the currently selected field value or empty string if no value is set.
14132 * @return {String} value The selected value
14134 getValue : function()
14136 if(Roo.isIOS && this.useNativeIOS){
14137 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14141 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14144 if(this.valueField){
14145 return typeof this.value != 'undefined' ? this.value : '';
14147 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14151 getRawValue : function()
14153 if(Roo.isIOS && this.useNativeIOS){
14154 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14157 var v = this.inputEl().getValue();
14163 * Clears any text/value currently set in the field
14165 clearValue : function(){
14167 if(this.hiddenField){
14168 this.hiddenField.dom.value = '';
14171 this.setRawValue('');
14172 this.lastSelectionText = '';
14173 this.lastData = false;
14175 var close = this.closeTriggerEl();
14186 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14187 * will be displayed in the field. If the value does not match the data value of an existing item,
14188 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14189 * Otherwise the field will be blank (although the value will still be set).
14190 * @param {String} value The value to match
14192 setValue : function(v)
14194 if(Roo.isIOS && this.useNativeIOS){
14195 this.setIOSValue(v);
14205 if(this.valueField){
14206 var r = this.findRecord(this.valueField, v);
14208 text = r.data[this.displayField];
14209 }else if(this.valueNotFoundText !== undefined){
14210 text = this.valueNotFoundText;
14213 this.lastSelectionText = text;
14214 if(this.hiddenField){
14215 this.hiddenField.dom.value = v;
14217 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14220 var close = this.closeTriggerEl();
14223 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14229 * @property {Object} the last set data for the element
14234 * Sets the value of the field based on a object which is related to the record format for the store.
14235 * @param {Object} value the value to set as. or false on reset?
14237 setFromData : function(o){
14244 var dv = ''; // display value
14245 var vv = ''; // value value..
14247 if (this.displayField) {
14248 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14250 // this is an error condition!!!
14251 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14254 if(this.valueField){
14255 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14258 var close = this.closeTriggerEl();
14261 if(dv.length || vv * 1 > 0){
14263 this.blockFocus=true;
14269 if(this.hiddenField){
14270 this.hiddenField.dom.value = vv;
14272 this.lastSelectionText = dv;
14273 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14277 // no hidden field.. - we store the value in 'value', but still display
14278 // display field!!!!
14279 this.lastSelectionText = dv;
14280 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14287 reset : function(){
14288 // overridden so that last data is reset..
14295 this.setValue(this.originalValue);
14296 //this.clearInvalid();
14297 this.lastData = false;
14299 this.view.clearSelections();
14305 findRecord : function(prop, value){
14307 if(this.store.getCount() > 0){
14308 this.store.each(function(r){
14309 if(r.data[prop] == value){
14319 getName: function()
14321 // returns hidden if it's set..
14322 if (!this.rendered) {return ''};
14323 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14327 onViewMove : function(e, t){
14328 this.inKeyMode = false;
14332 onViewOver : function(e, t){
14333 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14336 var item = this.view.findItemFromChild(t);
14339 var index = this.view.indexOf(item);
14340 this.select(index, false);
14345 onViewClick : function(view, doFocus, el, e)
14347 var index = this.view.getSelectedIndexes()[0];
14349 var r = this.store.getAt(index);
14353 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14360 Roo.each(this.tickItems, function(v,k){
14362 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14364 _this.tickItems.splice(k, 1);
14366 if(typeof(e) == 'undefined' && view == false){
14367 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14379 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14380 this.tickItems.push(r.data);
14383 if(typeof(e) == 'undefined' && view == false){
14384 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14391 this.onSelect(r, index);
14393 if(doFocus !== false && !this.blockFocus){
14394 this.inputEl().focus();
14399 restrictHeight : function(){
14400 //this.innerList.dom.style.height = '';
14401 //var inner = this.innerList.dom;
14402 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14403 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14404 //this.list.beginUpdate();
14405 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14406 this.list.alignTo(this.inputEl(), this.listAlign);
14407 this.list.alignTo(this.inputEl(), this.listAlign);
14408 //this.list.endUpdate();
14412 onEmptyResults : function(){
14414 if(this.tickable && this.editable){
14415 this.hasFocus = false;
14416 this.restrictHeight();
14424 * Returns true if the dropdown list is expanded, else false.
14426 isExpanded : function(){
14427 return this.list.isVisible();
14431 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14432 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14433 * @param {String} value The data value of the item to select
14434 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14435 * selected item if it is not currently in view (defaults to true)
14436 * @return {Boolean} True if the value matched an item in the list, else false
14438 selectByValue : function(v, scrollIntoView){
14439 if(v !== undefined && v !== null){
14440 var r = this.findRecord(this.valueField || this.displayField, v);
14442 this.select(this.store.indexOf(r), scrollIntoView);
14450 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14451 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14452 * @param {Number} index The zero-based index of the list item to select
14453 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14454 * selected item if it is not currently in view (defaults to true)
14456 select : function(index, scrollIntoView){
14457 this.selectedIndex = index;
14458 this.view.select(index);
14459 if(scrollIntoView !== false){
14460 var el = this.view.getNode(index);
14462 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14465 this.list.scrollChildIntoView(el, false);
14471 selectNext : function(){
14472 var ct = this.store.getCount();
14474 if(this.selectedIndex == -1){
14476 }else if(this.selectedIndex < ct-1){
14477 this.select(this.selectedIndex+1);
14483 selectPrev : function(){
14484 var ct = this.store.getCount();
14486 if(this.selectedIndex == -1){
14488 }else if(this.selectedIndex != 0){
14489 this.select(this.selectedIndex-1);
14495 onKeyUp : function(e){
14496 if(this.editable !== false && !e.isSpecialKey()){
14497 this.lastKey = e.getKey();
14498 this.dqTask.delay(this.queryDelay);
14503 validateBlur : function(){
14504 return !this.list || !this.list.isVisible();
14508 initQuery : function(){
14510 var v = this.getRawValue();
14512 if(this.tickable && this.editable){
14513 v = this.tickableInputEl().getValue();
14520 doForce : function(){
14521 if(this.inputEl().dom.value.length > 0){
14522 this.inputEl().dom.value =
14523 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14529 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14530 * query allowing the query action to be canceled if needed.
14531 * @param {String} query The SQL query to execute
14532 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14533 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14534 * saved in the current store (defaults to false)
14536 doQuery : function(q, forceAll){
14538 if(q === undefined || q === null){
14543 forceAll: forceAll,
14547 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14552 forceAll = qe.forceAll;
14553 if(forceAll === true || (q.length >= this.minChars)){
14555 this.hasQuery = true;
14557 if(this.lastQuery != q || this.alwaysQuery){
14558 this.lastQuery = q;
14559 if(this.mode == 'local'){
14560 this.selectedIndex = -1;
14562 this.store.clearFilter();
14565 if(this.specialFilter){
14566 this.fireEvent('specialfilter', this);
14571 this.store.filter(this.displayField, q);
14574 this.store.fireEvent("datachanged", this.store);
14581 this.store.baseParams[this.queryParam] = q;
14583 var options = {params : this.getParams(q)};
14586 options.add = true;
14587 options.params.start = this.page * this.pageSize;
14590 this.store.load(options);
14593 * this code will make the page width larger, at the beginning, the list not align correctly,
14594 * we should expand the list on onLoad
14595 * so command out it
14600 this.selectedIndex = -1;
14605 this.loadNext = false;
14609 getParams : function(q){
14611 //p[this.queryParam] = q;
14615 p.limit = this.pageSize;
14621 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14623 collapse : function(){
14624 if(!this.isExpanded()){
14630 this.hasFocus = false;
14634 this.cancelBtn.hide();
14635 this.trigger.show();
14638 this.tickableInputEl().dom.value = '';
14639 this.tickableInputEl().blur();
14644 Roo.get(document).un('mousedown', this.collapseIf, this);
14645 Roo.get(document).un('mousewheel', this.collapseIf, this);
14646 if (!this.editable) {
14647 Roo.get(document).un('keydown', this.listKeyPress, this);
14649 this.fireEvent('collapse', this);
14655 collapseIf : function(e){
14656 var in_combo = e.within(this.el);
14657 var in_list = e.within(this.list);
14658 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14660 if (in_combo || in_list || is_list) {
14661 //e.stopPropagation();
14666 this.onTickableFooterButtonClick(e, false, false);
14674 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14676 expand : function(){
14678 if(this.isExpanded() || !this.hasFocus){
14682 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14683 this.list.setWidth(lw);
14689 this.restrictHeight();
14693 this.tickItems = Roo.apply([], this.item);
14696 this.cancelBtn.show();
14697 this.trigger.hide();
14700 this.tickableInputEl().focus();
14705 Roo.get(document).on('mousedown', this.collapseIf, this);
14706 Roo.get(document).on('mousewheel', this.collapseIf, this);
14707 if (!this.editable) {
14708 Roo.get(document).on('keydown', this.listKeyPress, this);
14711 this.fireEvent('expand', this);
14715 // Implements the default empty TriggerField.onTriggerClick function
14716 onTriggerClick : function(e)
14718 Roo.log('trigger click');
14720 if(this.disabled || !this.triggerList){
14725 this.loadNext = false;
14727 if(this.isExpanded()){
14729 if (!this.blockFocus) {
14730 this.inputEl().focus();
14734 this.hasFocus = true;
14735 if(this.triggerAction == 'all') {
14736 this.doQuery(this.allQuery, true);
14738 this.doQuery(this.getRawValue());
14740 if (!this.blockFocus) {
14741 this.inputEl().focus();
14746 onTickableTriggerClick : function(e)
14753 this.loadNext = false;
14754 this.hasFocus = true;
14756 if(this.triggerAction == 'all') {
14757 this.doQuery(this.allQuery, true);
14759 this.doQuery(this.getRawValue());
14763 onSearchFieldClick : function(e)
14765 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14766 this.onTickableFooterButtonClick(e, false, false);
14770 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14775 this.loadNext = false;
14776 this.hasFocus = true;
14778 if(this.triggerAction == 'all') {
14779 this.doQuery(this.allQuery, true);
14781 this.doQuery(this.getRawValue());
14785 listKeyPress : function(e)
14787 //Roo.log('listkeypress');
14788 // scroll to first matching element based on key pres..
14789 if (e.isSpecialKey()) {
14792 var k = String.fromCharCode(e.getKey()).toUpperCase();
14795 var csel = this.view.getSelectedNodes();
14796 var cselitem = false;
14798 var ix = this.view.indexOf(csel[0]);
14799 cselitem = this.store.getAt(ix);
14800 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14806 this.store.each(function(v) {
14808 // start at existing selection.
14809 if (cselitem.id == v.id) {
14815 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14816 match = this.store.indexOf(v);
14822 if (match === false) {
14823 return true; // no more action?
14826 this.view.select(match);
14827 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14828 sn.scrollIntoView(sn.dom.parentNode, false);
14831 onViewScroll : function(e, t){
14833 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){
14837 this.hasQuery = true;
14839 this.loading = this.list.select('.loading', true).first();
14841 if(this.loading === null){
14842 this.list.createChild({
14844 cls: 'loading roo-select2-more-results roo-select2-active',
14845 html: 'Loading more results...'
14848 this.loading = this.list.select('.loading', true).first();
14850 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14852 this.loading.hide();
14855 this.loading.show();
14860 this.loadNext = true;
14862 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14867 addItem : function(o)
14869 var dv = ''; // display value
14871 if (this.displayField) {
14872 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14874 // this is an error condition!!!
14875 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14882 var choice = this.choices.createChild({
14884 cls: 'roo-select2-search-choice',
14893 cls: 'roo-select2-search-choice-close fa fa-times',
14898 }, this.searchField);
14900 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14902 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14910 this.inputEl().dom.value = '';
14915 onRemoveItem : function(e, _self, o)
14917 e.preventDefault();
14919 this.lastItem = Roo.apply([], this.item);
14921 var index = this.item.indexOf(o.data) * 1;
14924 Roo.log('not this item?!');
14928 this.item.splice(index, 1);
14933 this.fireEvent('remove', this, e);
14939 syncValue : function()
14941 if(!this.item.length){
14948 Roo.each(this.item, function(i){
14949 if(_this.valueField){
14950 value.push(i[_this.valueField]);
14957 this.value = value.join(',');
14959 if(this.hiddenField){
14960 this.hiddenField.dom.value = this.value;
14963 this.store.fireEvent("datachanged", this.store);
14968 clearItem : function()
14970 if(!this.multiple){
14976 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14984 if(this.tickable && !Roo.isTouch){
14985 this.view.refresh();
14989 inputEl: function ()
14991 if(Roo.isIOS && this.useNativeIOS){
14992 return this.el.select('select.roo-ios-select', true).first();
14995 if(Roo.isTouch && this.mobileTouchView){
14996 return this.el.select('input.form-control',true).first();
15000 return this.searchField;
15003 return this.el.select('input.form-control',true).first();
15006 onTickableFooterButtonClick : function(e, btn, el)
15008 e.preventDefault();
15010 this.lastItem = Roo.apply([], this.item);
15012 if(btn && btn.name == 'cancel'){
15013 this.tickItems = Roo.apply([], this.item);
15022 Roo.each(this.tickItems, function(o){
15030 validate : function()
15032 if(this.getVisibilityEl().hasClass('hidden')){
15036 var v = this.getRawValue();
15039 v = this.getValue();
15042 if(this.disabled || this.allowBlank || v.length){
15047 this.markInvalid();
15051 tickableInputEl : function()
15053 if(!this.tickable || !this.editable){
15054 return this.inputEl();
15057 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15061 getAutoCreateTouchView : function()
15066 cls: 'form-group' //input-group
15072 type : this.inputType,
15073 cls : 'form-control x-combo-noedit',
15074 autocomplete: 'new-password',
15075 placeholder : this.placeholder || '',
15080 input.name = this.name;
15084 input.cls += ' input-' + this.size;
15087 if (this.disabled) {
15088 input.disabled = true;
15099 inputblock.cls += ' input-group';
15101 inputblock.cn.unshift({
15103 cls : 'input-group-addon input-group-prepend input-group-text',
15108 if(this.removable && !this.multiple){
15109 inputblock.cls += ' roo-removable';
15111 inputblock.cn.push({
15114 cls : 'roo-combo-removable-btn close'
15118 if(this.hasFeedback && !this.allowBlank){
15120 inputblock.cls += ' has-feedback';
15122 inputblock.cn.push({
15124 cls: 'glyphicon form-control-feedback'
15131 inputblock.cls += (this.before) ? '' : ' input-group';
15133 inputblock.cn.push({
15135 cls : 'input-group-addon input-group-append input-group-text',
15141 var ibwrap = inputblock;
15146 cls: 'roo-select2-choices',
15150 cls: 'roo-select2-search-field',
15163 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15168 cls: 'form-hidden-field'
15174 if(!this.multiple && this.showToggleBtn){
15181 if (this.caret != false) {
15184 cls: 'fa fa-' + this.caret
15191 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15196 cls: 'combobox-clear',
15210 combobox.cls += ' roo-select2-container-multi';
15213 var align = this.labelAlign || this.parentLabelAlign();
15215 if (align ==='left' && this.fieldLabel.length) {
15220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15221 tooltip : 'This field is required'
15225 cls : 'control-label col-form-label',
15226 html : this.fieldLabel
15237 var labelCfg = cfg.cn[1];
15238 var contentCfg = cfg.cn[2];
15241 if(this.indicatorpos == 'right'){
15246 cls : 'control-label col-form-label',
15250 html : this.fieldLabel
15254 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15255 tooltip : 'This field is required'
15268 labelCfg = cfg.cn[0];
15269 contentCfg = cfg.cn[1];
15274 if(this.labelWidth > 12){
15275 labelCfg.style = "width: " + this.labelWidth + 'px';
15278 if(this.labelWidth < 13 && this.labelmd == 0){
15279 this.labelmd = this.labelWidth;
15282 if(this.labellg > 0){
15283 labelCfg.cls += ' col-lg-' + this.labellg;
15284 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15287 if(this.labelmd > 0){
15288 labelCfg.cls += ' col-md-' + this.labelmd;
15289 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15292 if(this.labelsm > 0){
15293 labelCfg.cls += ' col-sm-' + this.labelsm;
15294 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15297 if(this.labelxs > 0){
15298 labelCfg.cls += ' col-xs-' + this.labelxs;
15299 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15303 } else if ( this.fieldLabel.length) {
15307 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15308 tooltip : 'This field is required'
15312 cls : 'control-label',
15313 html : this.fieldLabel
15324 if(this.indicatorpos == 'right'){
15328 cls : 'control-label',
15329 html : this.fieldLabel,
15333 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15334 tooltip : 'This field is required'
15351 var settings = this;
15353 ['xs','sm','md','lg'].map(function(size){
15354 if (settings[size]) {
15355 cfg.cls += ' col-' + size + '-' + settings[size];
15362 initTouchView : function()
15364 this.renderTouchView();
15366 this.touchViewEl.on('scroll', function(){
15367 this.el.dom.scrollTop = 0;
15370 this.originalValue = this.getValue();
15372 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15374 this.inputEl().on("click", this.showTouchView, this);
15375 if (this.triggerEl) {
15376 this.triggerEl.on("click", this.showTouchView, this);
15380 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15381 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15383 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15385 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15386 this.store.on('load', this.onTouchViewLoad, this);
15387 this.store.on('loadexception', this.onTouchViewLoadException, this);
15389 if(this.hiddenName){
15391 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15393 this.hiddenField.dom.value =
15394 this.hiddenValue !== undefined ? this.hiddenValue :
15395 this.value !== undefined ? this.value : '';
15397 this.el.dom.removeAttribute('name');
15398 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15402 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15403 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15406 if(this.removable && !this.multiple){
15407 var close = this.closeTriggerEl();
15409 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15410 close.on('click', this.removeBtnClick, this, close);
15414 * fix the bug in Safari iOS8
15416 this.inputEl().on("focus", function(e){
15417 document.activeElement.blur();
15420 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15427 renderTouchView : function()
15429 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15430 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15432 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15433 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15435 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15436 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15437 this.touchViewBodyEl.setStyle('overflow', 'auto');
15439 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15440 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15442 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15443 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15447 showTouchView : function()
15453 this.touchViewHeaderEl.hide();
15455 if(this.modalTitle.length){
15456 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15457 this.touchViewHeaderEl.show();
15460 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15461 this.touchViewEl.show();
15463 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15465 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15466 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15468 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15470 if(this.modalTitle.length){
15471 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15474 this.touchViewBodyEl.setHeight(bodyHeight);
15478 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15480 this.touchViewEl.addClass('in');
15483 if(this._touchViewMask){
15484 Roo.get(document.body).addClass("x-body-masked");
15485 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15486 this._touchViewMask.setStyle('z-index', 10000);
15487 this._touchViewMask.addClass('show');
15490 this.doTouchViewQuery();
15494 hideTouchView : function()
15496 this.touchViewEl.removeClass('in');
15500 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15502 this.touchViewEl.setStyle('display', 'none');
15505 if(this._touchViewMask){
15506 this._touchViewMask.removeClass('show');
15507 Roo.get(document.body).removeClass("x-body-masked");
15511 setTouchViewValue : function()
15518 Roo.each(this.tickItems, function(o){
15523 this.hideTouchView();
15526 doTouchViewQuery : function()
15535 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15539 if(!this.alwaysQuery || this.mode == 'local'){
15540 this.onTouchViewLoad();
15547 onTouchViewBeforeLoad : function(combo,opts)
15553 onTouchViewLoad : function()
15555 if(this.store.getCount() < 1){
15556 this.onTouchViewEmptyResults();
15560 this.clearTouchView();
15562 var rawValue = this.getRawValue();
15564 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15566 this.tickItems = [];
15568 this.store.data.each(function(d, rowIndex){
15569 var row = this.touchViewListGroup.createChild(template);
15571 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15572 row.addClass(d.data.cls);
15575 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15578 html : d.data[this.displayField]
15581 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15582 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15585 row.removeClass('selected');
15586 if(!this.multiple && this.valueField &&
15587 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15590 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15591 row.addClass('selected');
15594 if(this.multiple && this.valueField &&
15595 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15599 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15600 this.tickItems.push(d.data);
15603 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15607 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15609 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15611 if(this.modalTitle.length){
15612 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15615 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15617 if(this.mobile_restrict_height && listHeight < bodyHeight){
15618 this.touchViewBodyEl.setHeight(listHeight);
15623 if(firstChecked && listHeight > bodyHeight){
15624 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15629 onTouchViewLoadException : function()
15631 this.hideTouchView();
15634 onTouchViewEmptyResults : function()
15636 this.clearTouchView();
15638 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15640 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15644 clearTouchView : function()
15646 this.touchViewListGroup.dom.innerHTML = '';
15649 onTouchViewClick : function(e, el, o)
15651 e.preventDefault();
15654 var rowIndex = o.rowIndex;
15656 var r = this.store.getAt(rowIndex);
15658 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15660 if(!this.multiple){
15661 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15662 c.dom.removeAttribute('checked');
15665 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15667 this.setFromData(r.data);
15669 var close = this.closeTriggerEl();
15675 this.hideTouchView();
15677 this.fireEvent('select', this, r, rowIndex);
15682 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15683 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15684 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15688 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15689 this.addItem(r.data);
15690 this.tickItems.push(r.data);
15694 getAutoCreateNativeIOS : function()
15697 cls: 'form-group' //input-group,
15702 cls : 'roo-ios-select'
15706 combobox.name = this.name;
15709 if (this.disabled) {
15710 combobox.disabled = true;
15713 var settings = this;
15715 ['xs','sm','md','lg'].map(function(size){
15716 if (settings[size]) {
15717 cfg.cls += ' col-' + size + '-' + settings[size];
15727 initIOSView : function()
15729 this.store.on('load', this.onIOSViewLoad, this);
15734 onIOSViewLoad : function()
15736 if(this.store.getCount() < 1){
15740 this.clearIOSView();
15742 if(this.allowBlank) {
15744 var default_text = '-- SELECT --';
15746 if(this.placeholder.length){
15747 default_text = this.placeholder;
15750 if(this.emptyTitle.length){
15751 default_text += ' - ' + this.emptyTitle + ' -';
15754 var opt = this.inputEl().createChild({
15757 html : default_text
15761 o[this.valueField] = 0;
15762 o[this.displayField] = default_text;
15764 this.ios_options.push({
15771 this.store.data.each(function(d, rowIndex){
15775 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15776 html = d.data[this.displayField];
15781 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15782 value = d.data[this.valueField];
15791 if(this.value == d.data[this.valueField]){
15792 option['selected'] = true;
15795 var opt = this.inputEl().createChild(option);
15797 this.ios_options.push({
15804 this.inputEl().on('change', function(){
15805 this.fireEvent('select', this);
15810 clearIOSView: function()
15812 this.inputEl().dom.innerHTML = '';
15814 this.ios_options = [];
15817 setIOSValue: function(v)
15821 if(!this.ios_options){
15825 Roo.each(this.ios_options, function(opts){
15827 opts.el.dom.removeAttribute('selected');
15829 if(opts.data[this.valueField] != v){
15833 opts.el.dom.setAttribute('selected', true);
15839 * @cfg {Boolean} grow
15843 * @cfg {Number} growMin
15847 * @cfg {Number} growMax
15856 Roo.apply(Roo.bootstrap.ComboBox, {
15860 cls: 'modal-header',
15882 cls: 'list-group-item',
15886 cls: 'roo-combobox-list-group-item-value'
15890 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15904 listItemCheckbox : {
15906 cls: 'list-group-item',
15910 cls: 'roo-combobox-list-group-item-value'
15914 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15930 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15935 cls: 'modal-footer',
15943 cls: 'col-xs-6 text-left',
15946 cls: 'btn btn-danger roo-touch-view-cancel',
15952 cls: 'col-xs-6 text-right',
15955 cls: 'btn btn-success roo-touch-view-ok',
15966 Roo.apply(Roo.bootstrap.ComboBox, {
15968 touchViewTemplate : {
15970 cls: 'modal fade roo-combobox-touch-view',
15974 cls: 'modal-dialog',
15975 style : 'position:fixed', // we have to fix position....
15979 cls: 'modal-content',
15981 Roo.bootstrap.ComboBox.header,
15982 Roo.bootstrap.ComboBox.body,
15983 Roo.bootstrap.ComboBox.footer
15992 * Ext JS Library 1.1.1
15993 * Copyright(c) 2006-2007, Ext JS, LLC.
15995 * Originally Released Under LGPL - original licence link has changed is not relivant.
15998 * <script type="text/javascript">
16003 * @extends Roo.util.Observable
16004 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16005 * This class also supports single and multi selection modes. <br>
16006 * Create a data model bound view:
16008 var store = new Roo.data.Store(...);
16010 var view = new Roo.View({
16012 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16014 singleSelect: true,
16015 selectedClass: "ydataview-selected",
16019 // listen for node click?
16020 view.on("click", function(vw, index, node, e){
16021 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16025 dataModel.load("foobar.xml");
16027 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16029 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16030 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16032 * Note: old style constructor is still suported (container, template, config)
16035 * Create a new View
16036 * @param {Object} config The config object
16039 Roo.View = function(config, depreciated_tpl, depreciated_config){
16041 this.parent = false;
16043 if (typeof(depreciated_tpl) == 'undefined') {
16044 // new way.. - universal constructor.
16045 Roo.apply(this, config);
16046 this.el = Roo.get(this.el);
16049 this.el = Roo.get(config);
16050 this.tpl = depreciated_tpl;
16051 Roo.apply(this, depreciated_config);
16053 this.wrapEl = this.el.wrap().wrap();
16054 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16057 if(typeof(this.tpl) == "string"){
16058 this.tpl = new Roo.Template(this.tpl);
16060 // support xtype ctors..
16061 this.tpl = new Roo.factory(this.tpl, Roo);
16065 this.tpl.compile();
16070 * @event beforeclick
16071 * Fires before a click is processed. Returns false to cancel the default action.
16072 * @param {Roo.View} this
16073 * @param {Number} index The index of the target node
16074 * @param {HTMLElement} node The target node
16075 * @param {Roo.EventObject} e The raw event object
16077 "beforeclick" : true,
16080 * Fires when a template node is clicked.
16081 * @param {Roo.View} this
16082 * @param {Number} index The index of the target node
16083 * @param {HTMLElement} node The target node
16084 * @param {Roo.EventObject} e The raw event object
16089 * Fires when a template node is double clicked.
16090 * @param {Roo.View} this
16091 * @param {Number} index The index of the target node
16092 * @param {HTMLElement} node The target node
16093 * @param {Roo.EventObject} e The raw event object
16097 * @event contextmenu
16098 * Fires when a template node is right clicked.
16099 * @param {Roo.View} this
16100 * @param {Number} index The index of the target node
16101 * @param {HTMLElement} node The target node
16102 * @param {Roo.EventObject} e The raw event object
16104 "contextmenu" : true,
16106 * @event selectionchange
16107 * Fires when the selected nodes change.
16108 * @param {Roo.View} this
16109 * @param {Array} selections Array of the selected nodes
16111 "selectionchange" : true,
16114 * @event beforeselect
16115 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16116 * @param {Roo.View} this
16117 * @param {HTMLElement} node The node to be selected
16118 * @param {Array} selections Array of currently selected nodes
16120 "beforeselect" : true,
16122 * @event preparedata
16123 * Fires on every row to render, to allow you to change the data.
16124 * @param {Roo.View} this
16125 * @param {Object} data to be rendered (change this)
16127 "preparedata" : true
16135 "click": this.onClick,
16136 "dblclick": this.onDblClick,
16137 "contextmenu": this.onContextMenu,
16141 this.selections = [];
16143 this.cmp = new Roo.CompositeElementLite([]);
16145 this.store = Roo.factory(this.store, Roo.data);
16146 this.setStore(this.store, true);
16149 if ( this.footer && this.footer.xtype) {
16151 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16153 this.footer.dataSource = this.store;
16154 this.footer.container = fctr;
16155 this.footer = Roo.factory(this.footer, Roo);
16156 fctr.insertFirst(this.el);
16158 // this is a bit insane - as the paging toolbar seems to detach the el..
16159 // dom.parentNode.parentNode.parentNode
16160 // they get detached?
16164 Roo.View.superclass.constructor.call(this);
16169 Roo.extend(Roo.View, Roo.util.Observable, {
16172 * @cfg {Roo.data.Store} store Data store to load data from.
16177 * @cfg {String|Roo.Element} el The container element.
16182 * @cfg {String|Roo.Template} tpl The template used by this View
16186 * @cfg {String} dataName the named area of the template to use as the data area
16187 * Works with domtemplates roo-name="name"
16191 * @cfg {String} selectedClass The css class to add to selected nodes
16193 selectedClass : "x-view-selected",
16195 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16200 * @cfg {String} text to display on mask (default Loading)
16204 * @cfg {Boolean} multiSelect Allow multiple selection
16206 multiSelect : false,
16208 * @cfg {Boolean} singleSelect Allow single selection
16210 singleSelect: false,
16213 * @cfg {Boolean} toggleSelect - selecting
16215 toggleSelect : false,
16218 * @cfg {Boolean} tickable - selecting
16223 * Returns the element this view is bound to.
16224 * @return {Roo.Element}
16226 getEl : function(){
16227 return this.wrapEl;
16233 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16235 refresh : function(){
16236 //Roo.log('refresh');
16239 // if we are using something like 'domtemplate', then
16240 // the what gets used is:
16241 // t.applySubtemplate(NAME, data, wrapping data..)
16242 // the outer template then get' applied with
16243 // the store 'extra data'
16244 // and the body get's added to the
16245 // roo-name="data" node?
16246 // <span class='roo-tpl-{name}'></span> ?????
16250 this.clearSelections();
16251 this.el.update("");
16253 var records = this.store.getRange();
16254 if(records.length < 1) {
16256 // is this valid?? = should it render a template??
16258 this.el.update(this.emptyText);
16262 if (this.dataName) {
16263 this.el.update(t.apply(this.store.meta)); //????
16264 el = this.el.child('.roo-tpl-' + this.dataName);
16267 for(var i = 0, len = records.length; i < len; i++){
16268 var data = this.prepareData(records[i].data, i, records[i]);
16269 this.fireEvent("preparedata", this, data, i, records[i]);
16271 var d = Roo.apply({}, data);
16274 Roo.apply(d, {'roo-id' : Roo.id()});
16278 Roo.each(this.parent.item, function(item){
16279 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16282 Roo.apply(d, {'roo-data-checked' : 'checked'});
16286 html[html.length] = Roo.util.Format.trim(
16288 t.applySubtemplate(this.dataName, d, this.store.meta) :
16295 el.update(html.join(""));
16296 this.nodes = el.dom.childNodes;
16297 this.updateIndexes(0);
16302 * Function to override to reformat the data that is sent to
16303 * the template for each node.
16304 * DEPRICATED - use the preparedata event handler.
16305 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16306 * a JSON object for an UpdateManager bound view).
16308 prepareData : function(data, index, record)
16310 this.fireEvent("preparedata", this, data, index, record);
16314 onUpdate : function(ds, record){
16315 // Roo.log('on update');
16316 this.clearSelections();
16317 var index = this.store.indexOf(record);
16318 var n = this.nodes[index];
16319 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16320 n.parentNode.removeChild(n);
16321 this.updateIndexes(index, index);
16327 onAdd : function(ds, records, index)
16329 //Roo.log(['on Add', ds, records, index] );
16330 this.clearSelections();
16331 if(this.nodes.length == 0){
16335 var n = this.nodes[index];
16336 for(var i = 0, len = records.length; i < len; i++){
16337 var d = this.prepareData(records[i].data, i, records[i]);
16339 this.tpl.insertBefore(n, d);
16342 this.tpl.append(this.el, d);
16345 this.updateIndexes(index);
16348 onRemove : function(ds, record, index){
16349 // Roo.log('onRemove');
16350 this.clearSelections();
16351 var el = this.dataName ?
16352 this.el.child('.roo-tpl-' + this.dataName) :
16355 el.dom.removeChild(this.nodes[index]);
16356 this.updateIndexes(index);
16360 * Refresh an individual node.
16361 * @param {Number} index
16363 refreshNode : function(index){
16364 this.onUpdate(this.store, this.store.getAt(index));
16367 updateIndexes : function(startIndex, endIndex){
16368 var ns = this.nodes;
16369 startIndex = startIndex || 0;
16370 endIndex = endIndex || ns.length - 1;
16371 for(var i = startIndex; i <= endIndex; i++){
16372 ns[i].nodeIndex = i;
16377 * Changes the data store this view uses and refresh the view.
16378 * @param {Store} store
16380 setStore : function(store, initial){
16381 if(!initial && this.store){
16382 this.store.un("datachanged", this.refresh);
16383 this.store.un("add", this.onAdd);
16384 this.store.un("remove", this.onRemove);
16385 this.store.un("update", this.onUpdate);
16386 this.store.un("clear", this.refresh);
16387 this.store.un("beforeload", this.onBeforeLoad);
16388 this.store.un("load", this.onLoad);
16389 this.store.un("loadexception", this.onLoad);
16393 store.on("datachanged", this.refresh, this);
16394 store.on("add", this.onAdd, this);
16395 store.on("remove", this.onRemove, this);
16396 store.on("update", this.onUpdate, this);
16397 store.on("clear", this.refresh, this);
16398 store.on("beforeload", this.onBeforeLoad, this);
16399 store.on("load", this.onLoad, this);
16400 store.on("loadexception", this.onLoad, this);
16408 * onbeforeLoad - masks the loading area.
16411 onBeforeLoad : function(store,opts)
16413 //Roo.log('onBeforeLoad');
16415 this.el.update("");
16417 this.el.mask(this.mask ? this.mask : "Loading" );
16419 onLoad : function ()
16426 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16427 * @param {HTMLElement} node
16428 * @return {HTMLElement} The template node
16430 findItemFromChild : function(node){
16431 var el = this.dataName ?
16432 this.el.child('.roo-tpl-' + this.dataName,true) :
16435 if(!node || node.parentNode == el){
16438 var p = node.parentNode;
16439 while(p && p != el){
16440 if(p.parentNode == el){
16449 onClick : function(e){
16450 var item = this.findItemFromChild(e.getTarget());
16452 var index = this.indexOf(item);
16453 if(this.onItemClick(item, index, e) !== false){
16454 this.fireEvent("click", this, index, item, e);
16457 this.clearSelections();
16462 onContextMenu : function(e){
16463 var item = this.findItemFromChild(e.getTarget());
16465 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16470 onDblClick : function(e){
16471 var item = this.findItemFromChild(e.getTarget());
16473 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16477 onItemClick : function(item, index, e)
16479 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16482 if (this.toggleSelect) {
16483 var m = this.isSelected(item) ? 'unselect' : 'select';
16486 _t[m](item, true, false);
16489 if(this.multiSelect || this.singleSelect){
16490 if(this.multiSelect && e.shiftKey && this.lastSelection){
16491 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16493 this.select(item, this.multiSelect && e.ctrlKey);
16494 this.lastSelection = item;
16497 if(!this.tickable){
16498 e.preventDefault();
16506 * Get the number of selected nodes.
16509 getSelectionCount : function(){
16510 return this.selections.length;
16514 * Get the currently selected nodes.
16515 * @return {Array} An array of HTMLElements
16517 getSelectedNodes : function(){
16518 return this.selections;
16522 * Get the indexes of the selected nodes.
16525 getSelectedIndexes : function(){
16526 var indexes = [], s = this.selections;
16527 for(var i = 0, len = s.length; i < len; i++){
16528 indexes.push(s[i].nodeIndex);
16534 * Clear all selections
16535 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16537 clearSelections : function(suppressEvent){
16538 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16539 this.cmp.elements = this.selections;
16540 this.cmp.removeClass(this.selectedClass);
16541 this.selections = [];
16542 if(!suppressEvent){
16543 this.fireEvent("selectionchange", this, this.selections);
16549 * Returns true if the passed node is selected
16550 * @param {HTMLElement/Number} node The node or node index
16551 * @return {Boolean}
16553 isSelected : function(node){
16554 var s = this.selections;
16558 node = this.getNode(node);
16559 return s.indexOf(node) !== -1;
16564 * @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
16565 * @param {Boolean} keepExisting (optional) true to keep existing selections
16566 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16568 select : function(nodeInfo, keepExisting, suppressEvent){
16569 if(nodeInfo instanceof Array){
16571 this.clearSelections(true);
16573 for(var i = 0, len = nodeInfo.length; i < len; i++){
16574 this.select(nodeInfo[i], true, true);
16578 var node = this.getNode(nodeInfo);
16579 if(!node || this.isSelected(node)){
16580 return; // already selected.
16583 this.clearSelections(true);
16586 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16587 Roo.fly(node).addClass(this.selectedClass);
16588 this.selections.push(node);
16589 if(!suppressEvent){
16590 this.fireEvent("selectionchange", this, this.selections);
16598 * @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
16599 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16600 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16602 unselect : function(nodeInfo, keepExisting, suppressEvent)
16604 if(nodeInfo instanceof Array){
16605 Roo.each(this.selections, function(s) {
16606 this.unselect(s, nodeInfo);
16610 var node = this.getNode(nodeInfo);
16611 if(!node || !this.isSelected(node)){
16612 //Roo.log("not selected");
16613 return; // not selected.
16617 Roo.each(this.selections, function(s) {
16619 Roo.fly(node).removeClass(this.selectedClass);
16626 this.selections= ns;
16627 this.fireEvent("selectionchange", this, this.selections);
16631 * Gets a template node.
16632 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16633 * @return {HTMLElement} The node or null if it wasn't found
16635 getNode : function(nodeInfo){
16636 if(typeof nodeInfo == "string"){
16637 return document.getElementById(nodeInfo);
16638 }else if(typeof nodeInfo == "number"){
16639 return this.nodes[nodeInfo];
16645 * Gets a range template nodes.
16646 * @param {Number} startIndex
16647 * @param {Number} endIndex
16648 * @return {Array} An array of nodes
16650 getNodes : function(start, end){
16651 var ns = this.nodes;
16652 start = start || 0;
16653 end = typeof end == "undefined" ? ns.length - 1 : end;
16656 for(var i = start; i <= end; i++){
16660 for(var i = start; i >= end; i--){
16668 * Finds the index of the passed node
16669 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16670 * @return {Number} The index of the node or -1
16672 indexOf : function(node){
16673 node = this.getNode(node);
16674 if(typeof node.nodeIndex == "number"){
16675 return node.nodeIndex;
16677 var ns = this.nodes;
16678 for(var i = 0, len = ns.length; i < len; i++){
16689 * based on jquery fullcalendar
16693 Roo.bootstrap = Roo.bootstrap || {};
16695 * @class Roo.bootstrap.Calendar
16696 * @extends Roo.bootstrap.Component
16697 * Bootstrap Calendar class
16698 * @cfg {Boolean} loadMask (true|false) default false
16699 * @cfg {Object} header generate the user specific header of the calendar, default false
16702 * Create a new Container
16703 * @param {Object} config The config object
16708 Roo.bootstrap.Calendar = function(config){
16709 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16713 * Fires when a date is selected
16714 * @param {DatePicker} this
16715 * @param {Date} date The selected date
16719 * @event monthchange
16720 * Fires when the displayed month changes
16721 * @param {DatePicker} this
16722 * @param {Date} date The selected month
16724 'monthchange': true,
16726 * @event evententer
16727 * Fires when mouse over an event
16728 * @param {Calendar} this
16729 * @param {event} Event
16731 'evententer': true,
16733 * @event eventleave
16734 * Fires when the mouse leaves an
16735 * @param {Calendar} this
16738 'eventleave': true,
16740 * @event eventclick
16741 * Fires when the mouse click an
16742 * @param {Calendar} this
16751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16754 * @cfg {Number} startDay
16755 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16763 getAutoCreate : function(){
16766 var fc_button = function(name, corner, style, content ) {
16767 return Roo.apply({},{
16769 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16771 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16774 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16785 style : 'width:100%',
16792 cls : 'fc-header-left',
16794 fc_button('prev', 'left', 'arrow', '‹' ),
16795 fc_button('next', 'right', 'arrow', '›' ),
16796 { tag: 'span', cls: 'fc-header-space' },
16797 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16805 cls : 'fc-header-center',
16809 cls: 'fc-header-title',
16812 html : 'month / year'
16820 cls : 'fc-header-right',
16822 /* fc_button('month', 'left', '', 'month' ),
16823 fc_button('week', '', '', 'week' ),
16824 fc_button('day', 'right', '', 'day' )
16836 header = this.header;
16839 var cal_heads = function() {
16841 // fixme - handle this.
16843 for (var i =0; i < Date.dayNames.length; i++) {
16844 var d = Date.dayNames[i];
16847 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16848 html : d.substring(0,3)
16852 ret[0].cls += ' fc-first';
16853 ret[6].cls += ' fc-last';
16856 var cal_cell = function(n) {
16859 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16864 cls: 'fc-day-number',
16868 cls: 'fc-day-content',
16872 style: 'position: relative;' // height: 17px;
16884 var cal_rows = function() {
16887 for (var r = 0; r < 6; r++) {
16894 for (var i =0; i < Date.dayNames.length; i++) {
16895 var d = Date.dayNames[i];
16896 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16899 row.cn[0].cls+=' fc-first';
16900 row.cn[0].cn[0].style = 'min-height:90px';
16901 row.cn[6].cls+=' fc-last';
16905 ret[0].cls += ' fc-first';
16906 ret[4].cls += ' fc-prev-last';
16907 ret[5].cls += ' fc-last';
16914 cls: 'fc-border-separate',
16915 style : 'width:100%',
16923 cls : 'fc-first fc-last',
16941 cls : 'fc-content',
16942 style : "position: relative;",
16945 cls : 'fc-view fc-view-month fc-grid',
16946 style : 'position: relative',
16947 unselectable : 'on',
16950 cls : 'fc-event-container',
16951 style : 'position:absolute;z-index:8;top:0;left:0;'
16969 initEvents : function()
16972 throw "can not find store for calendar";
16978 style: "text-align:center",
16982 style: "background-color:white;width:50%;margin:250 auto",
16986 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16997 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16999 var size = this.el.select('.fc-content', true).first().getSize();
17000 this.maskEl.setSize(size.width, size.height);
17001 this.maskEl.enableDisplayMode("block");
17002 if(!this.loadMask){
17003 this.maskEl.hide();
17006 this.store = Roo.factory(this.store, Roo.data);
17007 this.store.on('load', this.onLoad, this);
17008 this.store.on('beforeload', this.onBeforeLoad, this);
17012 this.cells = this.el.select('.fc-day',true);
17013 //Roo.log(this.cells);
17014 this.textNodes = this.el.query('.fc-day-number');
17015 this.cells.addClassOnOver('fc-state-hover');
17017 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17018 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17019 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17020 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17022 this.on('monthchange', this.onMonthChange, this);
17024 this.update(new Date().clearTime());
17027 resize : function() {
17028 var sz = this.el.getSize();
17030 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17031 this.el.select('.fc-day-content div',true).setHeight(34);
17036 showPrevMonth : function(e){
17037 this.update(this.activeDate.add("mo", -1));
17039 showToday : function(e){
17040 this.update(new Date().clearTime());
17043 showNextMonth : function(e){
17044 this.update(this.activeDate.add("mo", 1));
17048 showPrevYear : function(){
17049 this.update(this.activeDate.add("y", -1));
17053 showNextYear : function(){
17054 this.update(this.activeDate.add("y", 1));
17059 update : function(date)
17061 var vd = this.activeDate;
17062 this.activeDate = date;
17063 // if(vd && this.el){
17064 // var t = date.getTime();
17065 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17066 // Roo.log('using add remove');
17068 // this.fireEvent('monthchange', this, date);
17070 // this.cells.removeClass("fc-state-highlight");
17071 // this.cells.each(function(c){
17072 // if(c.dateValue == t){
17073 // c.addClass("fc-state-highlight");
17074 // setTimeout(function(){
17075 // try{c.dom.firstChild.focus();}catch(e){}
17085 var days = date.getDaysInMonth();
17087 var firstOfMonth = date.getFirstDateOfMonth();
17088 var startingPos = firstOfMonth.getDay()-this.startDay;
17090 if(startingPos < this.startDay){
17094 var pm = date.add(Date.MONTH, -1);
17095 var prevStart = pm.getDaysInMonth()-startingPos;
17097 this.cells = this.el.select('.fc-day',true);
17098 this.textNodes = this.el.query('.fc-day-number');
17099 this.cells.addClassOnOver('fc-state-hover');
17101 var cells = this.cells.elements;
17102 var textEls = this.textNodes;
17104 Roo.each(cells, function(cell){
17105 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17108 days += startingPos;
17110 // convert everything to numbers so it's fast
17111 var day = 86400000;
17112 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17115 //Roo.log(prevStart);
17117 var today = new Date().clearTime().getTime();
17118 var sel = date.clearTime().getTime();
17119 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17120 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17121 var ddMatch = this.disabledDatesRE;
17122 var ddText = this.disabledDatesText;
17123 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17124 var ddaysText = this.disabledDaysText;
17125 var format = this.format;
17127 var setCellClass = function(cal, cell){
17131 //Roo.log('set Cell Class');
17133 var t = d.getTime();
17137 cell.dateValue = t;
17139 cell.className += " fc-today";
17140 cell.className += " fc-state-highlight";
17141 cell.title = cal.todayText;
17144 // disable highlight in other month..
17145 //cell.className += " fc-state-highlight";
17150 cell.className = " fc-state-disabled";
17151 cell.title = cal.minText;
17155 cell.className = " fc-state-disabled";
17156 cell.title = cal.maxText;
17160 if(ddays.indexOf(d.getDay()) != -1){
17161 cell.title = ddaysText;
17162 cell.className = " fc-state-disabled";
17165 if(ddMatch && format){
17166 var fvalue = d.dateFormat(format);
17167 if(ddMatch.test(fvalue)){
17168 cell.title = ddText.replace("%0", fvalue);
17169 cell.className = " fc-state-disabled";
17173 if (!cell.initialClassName) {
17174 cell.initialClassName = cell.dom.className;
17177 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17182 for(; i < startingPos; i++) {
17183 textEls[i].innerHTML = (++prevStart);
17184 d.setDate(d.getDate()+1);
17186 cells[i].className = "fc-past fc-other-month";
17187 setCellClass(this, cells[i]);
17192 for(; i < days; i++){
17193 intDay = i - startingPos + 1;
17194 textEls[i].innerHTML = (intDay);
17195 d.setDate(d.getDate()+1);
17197 cells[i].className = ''; // "x-date-active";
17198 setCellClass(this, cells[i]);
17202 for(; i < 42; i++) {
17203 textEls[i].innerHTML = (++extraDays);
17204 d.setDate(d.getDate()+1);
17206 cells[i].className = "fc-future fc-other-month";
17207 setCellClass(this, cells[i]);
17210 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17212 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17214 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17215 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17217 if(totalRows != 6){
17218 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17219 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17222 this.fireEvent('monthchange', this, date);
17226 if(!this.internalRender){
17227 var main = this.el.dom.firstChild;
17228 var w = main.offsetWidth;
17229 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17230 Roo.fly(main).setWidth(w);
17231 this.internalRender = true;
17232 // opera does not respect the auto grow header center column
17233 // then, after it gets a width opera refuses to recalculate
17234 // without a second pass
17235 if(Roo.isOpera && !this.secondPass){
17236 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17237 this.secondPass = true;
17238 this.update.defer(10, this, [date]);
17245 findCell : function(dt) {
17246 dt = dt.clearTime().getTime();
17248 this.cells.each(function(c){
17249 //Roo.log("check " +c.dateValue + '?=' + dt);
17250 if(c.dateValue == dt){
17260 findCells : function(ev) {
17261 var s = ev.start.clone().clearTime().getTime();
17263 var e= ev.end.clone().clearTime().getTime();
17266 this.cells.each(function(c){
17267 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17269 if(c.dateValue > e){
17272 if(c.dateValue < s){
17281 // findBestRow: function(cells)
17285 // for (var i =0 ; i < cells.length;i++) {
17286 // ret = Math.max(cells[i].rows || 0,ret);
17293 addItem : function(ev)
17295 // look for vertical location slot in
17296 var cells = this.findCells(ev);
17298 // ev.row = this.findBestRow(cells);
17300 // work out the location.
17304 for(var i =0; i < cells.length; i++) {
17306 cells[i].row = cells[0].row;
17309 cells[i].row = cells[i].row + 1;
17319 if (crow.start.getY() == cells[i].getY()) {
17321 crow.end = cells[i];
17338 cells[0].events.push(ev);
17340 this.calevents.push(ev);
17343 clearEvents: function() {
17345 if(!this.calevents){
17349 Roo.each(this.cells.elements, function(c){
17355 Roo.each(this.calevents, function(e) {
17356 Roo.each(e.els, function(el) {
17357 el.un('mouseenter' ,this.onEventEnter, this);
17358 el.un('mouseleave' ,this.onEventLeave, this);
17363 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17369 renderEvents: function()
17373 this.cells.each(function(c) {
17382 if(c.row != c.events.length){
17383 r = 4 - (4 - (c.row - c.events.length));
17386 c.events = ev.slice(0, r);
17387 c.more = ev.slice(r);
17389 if(c.more.length && c.more.length == 1){
17390 c.events.push(c.more.pop());
17393 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17397 this.cells.each(function(c) {
17399 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17402 for (var e = 0; e < c.events.length; e++){
17403 var ev = c.events[e];
17404 var rows = ev.rows;
17406 for(var i = 0; i < rows.length; i++) {
17408 // how many rows should it span..
17411 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17412 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17414 unselectable : "on",
17417 cls: 'fc-event-inner',
17421 // cls: 'fc-event-time',
17422 // html : cells.length > 1 ? '' : ev.time
17426 cls: 'fc-event-title',
17427 html : String.format('{0}', ev.title)
17434 cls: 'ui-resizable-handle ui-resizable-e',
17435 html : '  '
17442 cfg.cls += ' fc-event-start';
17444 if ((i+1) == rows.length) {
17445 cfg.cls += ' fc-event-end';
17448 var ctr = _this.el.select('.fc-event-container',true).first();
17449 var cg = ctr.createChild(cfg);
17451 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17452 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17454 var r = (c.more.length) ? 1 : 0;
17455 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17456 cg.setWidth(ebox.right - sbox.x -2);
17458 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17459 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17460 cg.on('click', _this.onEventClick, _this, ev);
17471 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17472 style : 'position: absolute',
17473 unselectable : "on",
17476 cls: 'fc-event-inner',
17480 cls: 'fc-event-title',
17488 cls: 'ui-resizable-handle ui-resizable-e',
17489 html : '  '
17495 var ctr = _this.el.select('.fc-event-container',true).first();
17496 var cg = ctr.createChild(cfg);
17498 var sbox = c.select('.fc-day-content',true).first().getBox();
17499 var ebox = c.select('.fc-day-content',true).first().getBox();
17501 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17502 cg.setWidth(ebox.right - sbox.x -2);
17504 cg.on('click', _this.onMoreEventClick, _this, c.more);
17514 onEventEnter: function (e, el,event,d) {
17515 this.fireEvent('evententer', this, el, event);
17518 onEventLeave: function (e, el,event,d) {
17519 this.fireEvent('eventleave', this, el, event);
17522 onEventClick: function (e, el,event,d) {
17523 this.fireEvent('eventclick', this, el, event);
17526 onMonthChange: function () {
17530 onMoreEventClick: function(e, el, more)
17534 this.calpopover.placement = 'right';
17535 this.calpopover.setTitle('More');
17537 this.calpopover.setContent('');
17539 var ctr = this.calpopover.el.select('.popover-content', true).first();
17541 Roo.each(more, function(m){
17543 cls : 'fc-event-hori fc-event-draggable',
17546 var cg = ctr.createChild(cfg);
17548 cg.on('click', _this.onEventClick, _this, m);
17551 this.calpopover.show(el);
17556 onLoad: function ()
17558 this.calevents = [];
17561 if(this.store.getCount() > 0){
17562 this.store.data.each(function(d){
17565 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17566 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17567 time : d.data.start_time,
17568 title : d.data.title,
17569 description : d.data.description,
17570 venue : d.data.venue
17575 this.renderEvents();
17577 if(this.calevents.length && this.loadMask){
17578 this.maskEl.hide();
17582 onBeforeLoad: function()
17584 this.clearEvents();
17586 this.maskEl.show();
17600 * @class Roo.bootstrap.Popover
17601 * @extends Roo.bootstrap.Component
17602 * Bootstrap Popover class
17603 * @cfg {String} html contents of the popover (or false to use children..)
17604 * @cfg {String} title of popover (or false to hide)
17605 * @cfg {String} placement how it is placed
17606 * @cfg {String} trigger click || hover (or false to trigger manually)
17607 * @cfg {String} over what (parent or false to trigger manually.)
17608 * @cfg {Number} delay - delay before showing
17611 * Create a new Popover
17612 * @param {Object} config The config object
17615 Roo.bootstrap.Popover = function(config){
17616 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17622 * After the popover show
17624 * @param {Roo.bootstrap.Popover} this
17629 * After the popover hide
17631 * @param {Roo.bootstrap.Popover} this
17637 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17639 title: 'Fill in a title',
17642 placement : 'right',
17643 trigger : 'hover', // hover
17649 can_build_overlaid : false,
17651 getChildContainer : function()
17653 return this.el.select('.popover-content',true).first();
17656 getAutoCreate : function(){
17659 cls : 'popover roo-dynamic',
17660 style: 'display:block',
17666 cls : 'popover-inner',
17670 cls: 'popover-title popover-header',
17674 cls : 'popover-content popover-body',
17685 setTitle: function(str)
17688 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17690 setContent: function(str)
17693 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17695 // as it get's added to the bottom of the page.
17696 onRender : function(ct, position)
17698 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17700 var cfg = Roo.apply({}, this.getAutoCreate());
17704 cfg.cls += ' ' + this.cls;
17707 cfg.style = this.style;
17709 //Roo.log("adding to ");
17710 this.el = Roo.get(document.body).createChild(cfg, position);
17711 // Roo.log(this.el);
17716 initEvents : function()
17718 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17719 this.el.enableDisplayMode('block');
17721 if (this.over === false) {
17724 if (this.triggers === false) {
17727 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17728 var triggers = this.trigger ? this.trigger.split(' ') : [];
17729 Roo.each(triggers, function(trigger) {
17731 if (trigger == 'click') {
17732 on_el.on('click', this.toggle, this);
17733 } else if (trigger != 'manual') {
17734 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17735 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17737 on_el.on(eventIn ,this.enter, this);
17738 on_el.on(eventOut, this.leave, this);
17749 toggle : function () {
17750 this.hoverState == 'in' ? this.leave() : this.enter();
17753 enter : function () {
17755 clearTimeout(this.timeout);
17757 this.hoverState = 'in';
17759 if (!this.delay || !this.delay.show) {
17764 this.timeout = setTimeout(function () {
17765 if (_t.hoverState == 'in') {
17768 }, this.delay.show)
17771 leave : function() {
17772 clearTimeout(this.timeout);
17774 this.hoverState = 'out';
17776 if (!this.delay || !this.delay.hide) {
17781 this.timeout = setTimeout(function () {
17782 if (_t.hoverState == 'out') {
17785 }, this.delay.hide)
17788 show : function (on_el)
17791 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17795 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17796 if (this.html !== false) {
17797 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17799 this.el.removeClass([
17800 'fade','top','bottom', 'left', 'right','in',
17801 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17803 if (!this.title.length) {
17804 this.el.select('.popover-title',true).hide();
17807 var placement = typeof this.placement == 'function' ?
17808 this.placement.call(this, this.el, on_el) :
17811 var autoToken = /\s?auto?\s?/i;
17812 var autoPlace = autoToken.test(placement);
17814 placement = placement.replace(autoToken, '') || 'top';
17818 //this.el.setXY([0,0]);
17820 this.el.dom.style.display='block';
17821 this.el.addClass(placement);
17823 //this.el.appendTo(on_el);
17825 var p = this.getPosition();
17826 var box = this.el.getBox();
17831 var align = Roo.bootstrap.Popover.alignment[placement];
17834 this.el.alignTo(on_el, align[0],align[1]);
17835 //var arrow = this.el.select('.arrow',true).first();
17836 //arrow.set(align[2],
17838 this.el.addClass('in');
17841 if (this.el.hasClass('fade')) {
17845 this.hoverState = 'in';
17847 this.fireEvent('show', this);
17852 this.el.setXY([0,0]);
17853 this.el.removeClass('in');
17855 this.hoverState = null;
17857 this.fireEvent('hide', this);
17862 Roo.bootstrap.Popover.alignment = {
17863 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17864 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17865 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17866 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17877 * @class Roo.bootstrap.Progress
17878 * @extends Roo.bootstrap.Component
17879 * Bootstrap Progress class
17880 * @cfg {Boolean} striped striped of the progress bar
17881 * @cfg {Boolean} active animated of the progress bar
17885 * Create a new Progress
17886 * @param {Object} config The config object
17889 Roo.bootstrap.Progress = function(config){
17890 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17893 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17898 getAutoCreate : function(){
17906 cfg.cls += ' progress-striped';
17910 cfg.cls += ' active';
17929 * @class Roo.bootstrap.ProgressBar
17930 * @extends Roo.bootstrap.Component
17931 * Bootstrap ProgressBar class
17932 * @cfg {Number} aria_valuenow aria-value now
17933 * @cfg {Number} aria_valuemin aria-value min
17934 * @cfg {Number} aria_valuemax aria-value max
17935 * @cfg {String} label label for the progress bar
17936 * @cfg {String} panel (success | info | warning | danger )
17937 * @cfg {String} role role of the progress bar
17938 * @cfg {String} sr_only text
17942 * Create a new ProgressBar
17943 * @param {Object} config The config object
17946 Roo.bootstrap.ProgressBar = function(config){
17947 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17950 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17954 aria_valuemax : 100,
17960 getAutoCreate : function()
17965 cls: 'progress-bar',
17966 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17978 cfg.role = this.role;
17981 if(this.aria_valuenow){
17982 cfg['aria-valuenow'] = this.aria_valuenow;
17985 if(this.aria_valuemin){
17986 cfg['aria-valuemin'] = this.aria_valuemin;
17989 if(this.aria_valuemax){
17990 cfg['aria-valuemax'] = this.aria_valuemax;
17993 if(this.label && !this.sr_only){
17994 cfg.html = this.label;
17998 cfg.cls += ' progress-bar-' + this.panel;
18004 update : function(aria_valuenow)
18006 this.aria_valuenow = aria_valuenow;
18008 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18023 * @class Roo.bootstrap.TabGroup
18024 * @extends Roo.bootstrap.Column
18025 * Bootstrap Column class
18026 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18027 * @cfg {Boolean} carousel true to make the group behave like a carousel
18028 * @cfg {Boolean} bullets show bullets for the panels
18029 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18030 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18031 * @cfg {Boolean} showarrow (true|false) show arrow default true
18034 * Create a new TabGroup
18035 * @param {Object} config The config object
18038 Roo.bootstrap.TabGroup = function(config){
18039 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18041 this.navId = Roo.id();
18044 Roo.bootstrap.TabGroup.register(this);
18048 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18051 transition : false,
18056 slideOnTouch : false,
18059 getAutoCreate : function()
18061 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18063 cfg.cls += ' tab-content';
18065 if (this.carousel) {
18066 cfg.cls += ' carousel slide';
18069 cls : 'carousel-inner',
18073 if(this.bullets && !Roo.isTouch){
18076 cls : 'carousel-bullets',
18080 if(this.bullets_cls){
18081 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18088 cfg.cn[0].cn.push(bullets);
18091 if(this.showarrow){
18092 cfg.cn[0].cn.push({
18094 class : 'carousel-arrow',
18098 class : 'carousel-prev',
18102 class : 'fa fa-chevron-left'
18108 class : 'carousel-next',
18112 class : 'fa fa-chevron-right'
18125 initEvents: function()
18127 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18128 // this.el.on("touchstart", this.onTouchStart, this);
18131 if(this.autoslide){
18134 this.slideFn = window.setInterval(function() {
18135 _this.showPanelNext();
18139 if(this.showarrow){
18140 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18141 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18147 // onTouchStart : function(e, el, o)
18149 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18153 // this.showPanelNext();
18157 getChildContainer : function()
18159 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18163 * register a Navigation item
18164 * @param {Roo.bootstrap.NavItem} the navitem to add
18166 register : function(item)
18168 this.tabs.push( item);
18169 item.navId = this.navId; // not really needed..
18174 getActivePanel : function()
18177 Roo.each(this.tabs, function(t) {
18187 getPanelByName : function(n)
18190 Roo.each(this.tabs, function(t) {
18191 if (t.tabId == n) {
18199 indexOfPanel : function(p)
18202 Roo.each(this.tabs, function(t,i) {
18203 if (t.tabId == p.tabId) {
18212 * show a specific panel
18213 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18214 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18216 showPanel : function (pan)
18218 if(this.transition || typeof(pan) == 'undefined'){
18219 Roo.log("waiting for the transitionend");
18223 if (typeof(pan) == 'number') {
18224 pan = this.tabs[pan];
18227 if (typeof(pan) == 'string') {
18228 pan = this.getPanelByName(pan);
18231 var cur = this.getActivePanel();
18234 Roo.log('pan or acitve pan is undefined');
18238 if (pan.tabId == this.getActivePanel().tabId) {
18242 if (false === cur.fireEvent('beforedeactivate')) {
18246 if(this.bullets > 0 && !Roo.isTouch){
18247 this.setActiveBullet(this.indexOfPanel(pan));
18250 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18252 this.transition = true;
18253 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18254 var lr = dir == 'next' ? 'left' : 'right';
18255 pan.el.addClass(dir); // or prev
18256 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18257 cur.el.addClass(lr); // or right
18258 pan.el.addClass(lr);
18261 cur.el.on('transitionend', function() {
18262 Roo.log("trans end?");
18264 pan.el.removeClass([lr,dir]);
18265 pan.setActive(true);
18267 cur.el.removeClass([lr]);
18268 cur.setActive(false);
18270 _this.transition = false;
18272 }, this, { single: true } );
18277 cur.setActive(false);
18278 pan.setActive(true);
18283 showPanelNext : function()
18285 var i = this.indexOfPanel(this.getActivePanel());
18287 if (i >= this.tabs.length - 1 && !this.autoslide) {
18291 if (i >= this.tabs.length - 1 && this.autoslide) {
18295 this.showPanel(this.tabs[i+1]);
18298 showPanelPrev : function()
18300 var i = this.indexOfPanel(this.getActivePanel());
18302 if (i < 1 && !this.autoslide) {
18306 if (i < 1 && this.autoslide) {
18307 i = this.tabs.length;
18310 this.showPanel(this.tabs[i-1]);
18314 addBullet: function()
18316 if(!this.bullets || Roo.isTouch){
18319 var ctr = this.el.select('.carousel-bullets',true).first();
18320 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18321 var bullet = ctr.createChild({
18322 cls : 'bullet bullet-' + i
18323 },ctr.dom.lastChild);
18328 bullet.on('click', (function(e, el, o, ii, t){
18330 e.preventDefault();
18332 this.showPanel(ii);
18334 if(this.autoslide && this.slideFn){
18335 clearInterval(this.slideFn);
18336 this.slideFn = window.setInterval(function() {
18337 _this.showPanelNext();
18341 }).createDelegate(this, [i, bullet], true));
18346 setActiveBullet : function(i)
18352 Roo.each(this.el.select('.bullet', true).elements, function(el){
18353 el.removeClass('selected');
18356 var bullet = this.el.select('.bullet-' + i, true).first();
18362 bullet.addClass('selected');
18373 Roo.apply(Roo.bootstrap.TabGroup, {
18377 * register a Navigation Group
18378 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18380 register : function(navgrp)
18382 this.groups[navgrp.navId] = navgrp;
18386 * fetch a Navigation Group based on the navigation ID
18387 * if one does not exist , it will get created.
18388 * @param {string} the navgroup to add
18389 * @returns {Roo.bootstrap.NavGroup} the navgroup
18391 get: function(navId) {
18392 if (typeof(this.groups[navId]) == 'undefined') {
18393 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18395 return this.groups[navId] ;
18410 * @class Roo.bootstrap.TabPanel
18411 * @extends Roo.bootstrap.Component
18412 * Bootstrap TabPanel class
18413 * @cfg {Boolean} active panel active
18414 * @cfg {String} html panel content
18415 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18416 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18417 * @cfg {String} href click to link..
18421 * Create a new TabPanel
18422 * @param {Object} config The config object
18425 Roo.bootstrap.TabPanel = function(config){
18426 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18430 * Fires when the active status changes
18431 * @param {Roo.bootstrap.TabPanel} this
18432 * @param {Boolean} state the new state
18437 * @event beforedeactivate
18438 * Fires before a tab is de-activated - can be used to do validation on a form.
18439 * @param {Roo.bootstrap.TabPanel} this
18440 * @return {Boolean} false if there is an error
18443 'beforedeactivate': true
18446 this.tabId = this.tabId || Roo.id();
18450 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18458 getAutoCreate : function(){
18461 // item is needed for carousel - not sure if it has any effect otherwise
18462 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18463 html: this.html || ''
18467 cfg.cls += ' active';
18471 cfg.tabId = this.tabId;
18478 initEvents: function()
18480 var p = this.parent();
18482 this.navId = this.navId || p.navId;
18484 if (typeof(this.navId) != 'undefined') {
18485 // not really needed.. but just in case.. parent should be a NavGroup.
18486 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18490 var i = tg.tabs.length - 1;
18492 if(this.active && tg.bullets > 0 && i < tg.bullets){
18493 tg.setActiveBullet(i);
18497 this.el.on('click', this.onClick, this);
18500 this.el.on("touchstart", this.onTouchStart, this);
18501 this.el.on("touchmove", this.onTouchMove, this);
18502 this.el.on("touchend", this.onTouchEnd, this);
18507 onRender : function(ct, position)
18509 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18512 setActive : function(state)
18514 Roo.log("panel - set active " + this.tabId + "=" + state);
18516 this.active = state;
18518 this.el.removeClass('active');
18520 } else if (!this.el.hasClass('active')) {
18521 this.el.addClass('active');
18524 this.fireEvent('changed', this, state);
18527 onClick : function(e)
18529 e.preventDefault();
18531 if(!this.href.length){
18535 window.location.href = this.href;
18544 onTouchStart : function(e)
18546 this.swiping = false;
18548 this.startX = e.browserEvent.touches[0].clientX;
18549 this.startY = e.browserEvent.touches[0].clientY;
18552 onTouchMove : function(e)
18554 this.swiping = true;
18556 this.endX = e.browserEvent.touches[0].clientX;
18557 this.endY = e.browserEvent.touches[0].clientY;
18560 onTouchEnd : function(e)
18567 var tabGroup = this.parent();
18569 if(this.endX > this.startX){ // swiping right
18570 tabGroup.showPanelPrev();
18574 if(this.startX > this.endX){ // swiping left
18575 tabGroup.showPanelNext();
18594 * @class Roo.bootstrap.DateField
18595 * @extends Roo.bootstrap.Input
18596 * Bootstrap DateField class
18597 * @cfg {Number} weekStart default 0
18598 * @cfg {String} viewMode default empty, (months|years)
18599 * @cfg {String} minViewMode default empty, (months|years)
18600 * @cfg {Number} startDate default -Infinity
18601 * @cfg {Number} endDate default Infinity
18602 * @cfg {Boolean} todayHighlight default false
18603 * @cfg {Boolean} todayBtn default false
18604 * @cfg {Boolean} calendarWeeks default false
18605 * @cfg {Object} daysOfWeekDisabled default empty
18606 * @cfg {Boolean} singleMode default false (true | false)
18608 * @cfg {Boolean} keyboardNavigation default true
18609 * @cfg {String} language default en
18612 * Create a new DateField
18613 * @param {Object} config The config object
18616 Roo.bootstrap.DateField = function(config){
18617 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18621 * Fires when this field show.
18622 * @param {Roo.bootstrap.DateField} this
18623 * @param {Mixed} date The date value
18628 * Fires when this field hide.
18629 * @param {Roo.bootstrap.DateField} this
18630 * @param {Mixed} date The date value
18635 * Fires when select a date.
18636 * @param {Roo.bootstrap.DateField} this
18637 * @param {Mixed} date The date value
18641 * @event beforeselect
18642 * Fires when before select a date.
18643 * @param {Roo.bootstrap.DateField} this
18644 * @param {Mixed} date The date value
18646 beforeselect : true
18650 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18653 * @cfg {String} format
18654 * The default date format string which can be overriden for localization support. The format must be
18655 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18659 * @cfg {String} altFormats
18660 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18661 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18663 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18671 todayHighlight : false,
18677 keyboardNavigation: true,
18679 calendarWeeks: false,
18681 startDate: -Infinity,
18685 daysOfWeekDisabled: [],
18689 singleMode : false,
18691 UTCDate: function()
18693 return new Date(Date.UTC.apply(Date, arguments));
18696 UTCToday: function()
18698 var today = new Date();
18699 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18702 getDate: function() {
18703 var d = this.getUTCDate();
18704 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18707 getUTCDate: function() {
18711 setDate: function(d) {
18712 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18715 setUTCDate: function(d) {
18717 this.setValue(this.formatDate(this.date));
18720 onRender: function(ct, position)
18723 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18725 this.language = this.language || 'en';
18726 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18727 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18729 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18730 this.format = this.format || 'm/d/y';
18731 this.isInline = false;
18732 this.isInput = true;
18733 this.component = this.el.select('.add-on', true).first() || false;
18734 this.component = (this.component && this.component.length === 0) ? false : this.component;
18735 this.hasInput = this.component && this.inputEl().length;
18737 if (typeof(this.minViewMode === 'string')) {
18738 switch (this.minViewMode) {
18740 this.minViewMode = 1;
18743 this.minViewMode = 2;
18746 this.minViewMode = 0;
18751 if (typeof(this.viewMode === 'string')) {
18752 switch (this.viewMode) {
18765 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18767 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18769 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18771 this.picker().on('mousedown', this.onMousedown, this);
18772 this.picker().on('click', this.onClick, this);
18774 this.picker().addClass('datepicker-dropdown');
18776 this.startViewMode = this.viewMode;
18778 if(this.singleMode){
18779 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18780 v.setVisibilityMode(Roo.Element.DISPLAY);
18784 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18785 v.setStyle('width', '189px');
18789 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18790 if(!this.calendarWeeks){
18795 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18796 v.attr('colspan', function(i, val){
18797 return parseInt(val) + 1;
18802 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18804 this.setStartDate(this.startDate);
18805 this.setEndDate(this.endDate);
18807 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18814 if(this.isInline) {
18819 picker : function()
18821 return this.pickerEl;
18822 // return this.el.select('.datepicker', true).first();
18825 fillDow: function()
18827 var dowCnt = this.weekStart;
18836 if(this.calendarWeeks){
18844 while (dowCnt < this.weekStart + 7) {
18848 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18852 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18855 fillMonths: function()
18858 var months = this.picker().select('>.datepicker-months td', true).first();
18860 months.dom.innerHTML = '';
18866 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18869 months.createChild(month);
18876 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;
18878 if (this.date < this.startDate) {
18879 this.viewDate = new Date(this.startDate);
18880 } else if (this.date > this.endDate) {
18881 this.viewDate = new Date(this.endDate);
18883 this.viewDate = new Date(this.date);
18891 var d = new Date(this.viewDate),
18892 year = d.getUTCFullYear(),
18893 month = d.getUTCMonth(),
18894 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18895 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18896 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18897 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18898 currentDate = this.date && this.date.valueOf(),
18899 today = this.UTCToday();
18901 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18903 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18905 // this.picker.select('>tfoot th.today').
18906 // .text(dates[this.language].today)
18907 // .toggle(this.todayBtn !== false);
18909 this.updateNavArrows();
18912 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18914 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18916 prevMonth.setUTCDate(day);
18918 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18920 var nextMonth = new Date(prevMonth);
18922 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18924 nextMonth = nextMonth.valueOf();
18926 var fillMonths = false;
18928 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18930 while(prevMonth.valueOf() <= nextMonth) {
18933 if (prevMonth.getUTCDay() === this.weekStart) {
18935 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18943 if(this.calendarWeeks){
18944 // ISO 8601: First week contains first thursday.
18945 // ISO also states week starts on Monday, but we can be more abstract here.
18947 // Start of current week: based on weekstart/current date
18948 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18949 // Thursday of this week
18950 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18951 // First Thursday of year, year from thursday
18952 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18953 // Calendar week: ms between thursdays, div ms per day, div 7 days
18954 calWeek = (th - yth) / 864e5 / 7 + 1;
18956 fillMonths.cn.push({
18964 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18966 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18969 if (this.todayHighlight &&
18970 prevMonth.getUTCFullYear() == today.getFullYear() &&
18971 prevMonth.getUTCMonth() == today.getMonth() &&
18972 prevMonth.getUTCDate() == today.getDate()) {
18973 clsName += ' today';
18976 if (currentDate && prevMonth.valueOf() === currentDate) {
18977 clsName += ' active';
18980 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18981 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18982 clsName += ' disabled';
18985 fillMonths.cn.push({
18987 cls: 'day ' + clsName,
18988 html: prevMonth.getDate()
18991 prevMonth.setDate(prevMonth.getDate()+1);
18994 var currentYear = this.date && this.date.getUTCFullYear();
18995 var currentMonth = this.date && this.date.getUTCMonth();
18997 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18999 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19000 v.removeClass('active');
19002 if(currentYear === year && k === currentMonth){
19003 v.addClass('active');
19006 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19007 v.addClass('disabled');
19013 year = parseInt(year/10, 10) * 10;
19015 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19017 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19020 for (var i = -1; i < 11; i++) {
19021 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19023 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19031 showMode: function(dir)
19034 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19037 Roo.each(this.picker().select('>div',true).elements, function(v){
19038 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19041 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19046 if(this.isInline) {
19050 this.picker().removeClass(['bottom', 'top']);
19052 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19054 * place to the top of element!
19058 this.picker().addClass('top');
19059 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19064 this.picker().addClass('bottom');
19066 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19069 parseDate : function(value)
19071 if(!value || value instanceof Date){
19074 var v = Date.parseDate(value, this.format);
19075 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19076 v = Date.parseDate(value, 'Y-m-d');
19078 if(!v && this.altFormats){
19079 if(!this.altFormatsArray){
19080 this.altFormatsArray = this.altFormats.split("|");
19082 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19083 v = Date.parseDate(value, this.altFormatsArray[i]);
19089 formatDate : function(date, fmt)
19091 return (!date || !(date instanceof Date)) ?
19092 date : date.dateFormat(fmt || this.format);
19095 onFocus : function()
19097 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19101 onBlur : function()
19103 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19105 var d = this.inputEl().getValue();
19112 showPopup : function()
19114 this.picker().show();
19118 this.fireEvent('showpopup', this, this.date);
19121 hidePopup : function()
19123 if(this.isInline) {
19126 this.picker().hide();
19127 this.viewMode = this.startViewMode;
19130 this.fireEvent('hidepopup', this, this.date);
19134 onMousedown: function(e)
19136 e.stopPropagation();
19137 e.preventDefault();
19142 Roo.bootstrap.DateField.superclass.keyup.call(this);
19146 setValue: function(v)
19148 if(this.fireEvent('beforeselect', this, v) !== false){
19149 var d = new Date(this.parseDate(v) ).clearTime();
19151 if(isNaN(d.getTime())){
19152 this.date = this.viewDate = '';
19153 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19157 v = this.formatDate(d);
19159 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19161 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19165 this.fireEvent('select', this, this.date);
19169 getValue: function()
19171 return this.formatDate(this.date);
19174 fireKey: function(e)
19176 if (!this.picker().isVisible()){
19177 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19183 var dateChanged = false,
19185 newDate, newViewDate;
19190 e.preventDefault();
19194 if (!this.keyboardNavigation) {
19197 dir = e.keyCode == 37 ? -1 : 1;
19200 newDate = this.moveYear(this.date, dir);
19201 newViewDate = this.moveYear(this.viewDate, dir);
19202 } else if (e.shiftKey){
19203 newDate = this.moveMonth(this.date, dir);
19204 newViewDate = this.moveMonth(this.viewDate, dir);
19206 newDate = new Date(this.date);
19207 newDate.setUTCDate(this.date.getUTCDate() + dir);
19208 newViewDate = new Date(this.viewDate);
19209 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19211 if (this.dateWithinRange(newDate)){
19212 this.date = newDate;
19213 this.viewDate = newViewDate;
19214 this.setValue(this.formatDate(this.date));
19216 e.preventDefault();
19217 dateChanged = true;
19222 if (!this.keyboardNavigation) {
19225 dir = e.keyCode == 38 ? -1 : 1;
19227 newDate = this.moveYear(this.date, dir);
19228 newViewDate = this.moveYear(this.viewDate, dir);
19229 } else if (e.shiftKey){
19230 newDate = this.moveMonth(this.date, dir);
19231 newViewDate = this.moveMonth(this.viewDate, dir);
19233 newDate = new Date(this.date);
19234 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19235 newViewDate = new Date(this.viewDate);
19236 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19238 if (this.dateWithinRange(newDate)){
19239 this.date = newDate;
19240 this.viewDate = newViewDate;
19241 this.setValue(this.formatDate(this.date));
19243 e.preventDefault();
19244 dateChanged = true;
19248 this.setValue(this.formatDate(this.date));
19250 e.preventDefault();
19253 this.setValue(this.formatDate(this.date));
19267 onClick: function(e)
19269 e.stopPropagation();
19270 e.preventDefault();
19272 var target = e.getTarget();
19274 if(target.nodeName.toLowerCase() === 'i'){
19275 target = Roo.get(target).dom.parentNode;
19278 var nodeName = target.nodeName;
19279 var className = target.className;
19280 var html = target.innerHTML;
19281 //Roo.log(nodeName);
19283 switch(nodeName.toLowerCase()) {
19285 switch(className) {
19291 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19292 switch(this.viewMode){
19294 this.viewDate = this.moveMonth(this.viewDate, dir);
19298 this.viewDate = this.moveYear(this.viewDate, dir);
19304 var date = new Date();
19305 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19307 this.setValue(this.formatDate(this.date));
19314 if (className.indexOf('disabled') < 0) {
19315 this.viewDate.setUTCDate(1);
19316 if (className.indexOf('month') > -1) {
19317 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19319 var year = parseInt(html, 10) || 0;
19320 this.viewDate.setUTCFullYear(year);
19324 if(this.singleMode){
19325 this.setValue(this.formatDate(this.viewDate));
19336 //Roo.log(className);
19337 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19338 var day = parseInt(html, 10) || 1;
19339 var year = this.viewDate.getUTCFullYear(),
19340 month = this.viewDate.getUTCMonth();
19342 if (className.indexOf('old') > -1) {
19349 } else if (className.indexOf('new') > -1) {
19357 //Roo.log([year,month,day]);
19358 this.date = this.UTCDate(year, month, day,0,0,0,0);
19359 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19361 //Roo.log(this.formatDate(this.date));
19362 this.setValue(this.formatDate(this.date));
19369 setStartDate: function(startDate)
19371 this.startDate = startDate || -Infinity;
19372 if (this.startDate !== -Infinity) {
19373 this.startDate = this.parseDate(this.startDate);
19376 this.updateNavArrows();
19379 setEndDate: function(endDate)
19381 this.endDate = endDate || Infinity;
19382 if (this.endDate !== Infinity) {
19383 this.endDate = this.parseDate(this.endDate);
19386 this.updateNavArrows();
19389 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19391 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19392 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19393 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19395 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19396 return parseInt(d, 10);
19399 this.updateNavArrows();
19402 updateNavArrows: function()
19404 if(this.singleMode){
19408 var d = new Date(this.viewDate),
19409 year = d.getUTCFullYear(),
19410 month = d.getUTCMonth();
19412 Roo.each(this.picker().select('.prev', true).elements, function(v){
19414 switch (this.viewMode) {
19417 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19423 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19430 Roo.each(this.picker().select('.next', true).elements, function(v){
19432 switch (this.viewMode) {
19435 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19441 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19449 moveMonth: function(date, dir)
19454 var new_date = new Date(date.valueOf()),
19455 day = new_date.getUTCDate(),
19456 month = new_date.getUTCMonth(),
19457 mag = Math.abs(dir),
19459 dir = dir > 0 ? 1 : -1;
19462 // If going back one month, make sure month is not current month
19463 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19465 return new_date.getUTCMonth() == month;
19467 // If going forward one month, make sure month is as expected
19468 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19470 return new_date.getUTCMonth() != new_month;
19472 new_month = month + dir;
19473 new_date.setUTCMonth(new_month);
19474 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19475 if (new_month < 0 || new_month > 11) {
19476 new_month = (new_month + 12) % 12;
19479 // For magnitudes >1, move one month at a time...
19480 for (var i=0; i<mag; i++) {
19481 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19482 new_date = this.moveMonth(new_date, dir);
19484 // ...then reset the day, keeping it in the new month
19485 new_month = new_date.getUTCMonth();
19486 new_date.setUTCDate(day);
19488 return new_month != new_date.getUTCMonth();
19491 // Common date-resetting loop -- if date is beyond end of month, make it
19494 new_date.setUTCDate(--day);
19495 new_date.setUTCMonth(new_month);
19500 moveYear: function(date, dir)
19502 return this.moveMonth(date, dir*12);
19505 dateWithinRange: function(date)
19507 return date >= this.startDate && date <= this.endDate;
19513 this.picker().remove();
19516 validateValue : function(value)
19518 if(this.getVisibilityEl().hasClass('hidden')){
19522 if(value.length < 1) {
19523 if(this.allowBlank){
19529 if(value.length < this.minLength){
19532 if(value.length > this.maxLength){
19536 var vt = Roo.form.VTypes;
19537 if(!vt[this.vtype](value, this)){
19541 if(typeof this.validator == "function"){
19542 var msg = this.validator(value);
19548 if(this.regex && !this.regex.test(value)){
19552 if(typeof(this.parseDate(value)) == 'undefined'){
19556 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19560 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19570 this.date = this.viewDate = '';
19572 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19577 Roo.apply(Roo.bootstrap.DateField, {
19588 html: '<i class="fa fa-arrow-left"/>'
19598 html: '<i class="fa fa-arrow-right"/>'
19640 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19641 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19642 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19643 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19644 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19657 navFnc: 'FullYear',
19662 navFnc: 'FullYear',
19667 Roo.apply(Roo.bootstrap.DateField, {
19671 cls: 'datepicker dropdown-menu roo-dynamic',
19675 cls: 'datepicker-days',
19679 cls: 'table-condensed',
19681 Roo.bootstrap.DateField.head,
19685 Roo.bootstrap.DateField.footer
19692 cls: 'datepicker-months',
19696 cls: 'table-condensed',
19698 Roo.bootstrap.DateField.head,
19699 Roo.bootstrap.DateField.content,
19700 Roo.bootstrap.DateField.footer
19707 cls: 'datepicker-years',
19711 cls: 'table-condensed',
19713 Roo.bootstrap.DateField.head,
19714 Roo.bootstrap.DateField.content,
19715 Roo.bootstrap.DateField.footer
19734 * @class Roo.bootstrap.TimeField
19735 * @extends Roo.bootstrap.Input
19736 * Bootstrap DateField class
19740 * Create a new TimeField
19741 * @param {Object} config The config object
19744 Roo.bootstrap.TimeField = function(config){
19745 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19749 * Fires when this field show.
19750 * @param {Roo.bootstrap.DateField} thisthis
19751 * @param {Mixed} date The date value
19756 * Fires when this field hide.
19757 * @param {Roo.bootstrap.DateField} this
19758 * @param {Mixed} date The date value
19763 * Fires when select a date.
19764 * @param {Roo.bootstrap.DateField} this
19765 * @param {Mixed} date The date value
19771 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19774 * @cfg {String} format
19775 * The default time format string which can be overriden for localization support. The format must be
19776 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19780 onRender: function(ct, position)
19783 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19785 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19787 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19789 this.pop = this.picker().select('>.datepicker-time',true).first();
19790 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19792 this.picker().on('mousedown', this.onMousedown, this);
19793 this.picker().on('click', this.onClick, this);
19795 this.picker().addClass('datepicker-dropdown');
19800 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19801 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19802 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19803 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19804 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19805 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19809 fireKey: function(e){
19810 if (!this.picker().isVisible()){
19811 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19817 e.preventDefault();
19825 this.onTogglePeriod();
19828 this.onIncrementMinutes();
19831 this.onDecrementMinutes();
19840 onClick: function(e) {
19841 e.stopPropagation();
19842 e.preventDefault();
19845 picker : function()
19847 return this.el.select('.datepicker', true).first();
19850 fillTime: function()
19852 var time = this.pop.select('tbody', true).first();
19854 time.dom.innerHTML = '';
19869 cls: 'hours-up glyphicon glyphicon-chevron-up'
19889 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19910 cls: 'timepicker-hour',
19925 cls: 'timepicker-minute',
19940 cls: 'btn btn-primary period',
19962 cls: 'hours-down glyphicon glyphicon-chevron-down'
19982 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20000 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20007 var hours = this.time.getHours();
20008 var minutes = this.time.getMinutes();
20021 hours = hours - 12;
20025 hours = '0' + hours;
20029 minutes = '0' + minutes;
20032 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20033 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20034 this.pop.select('button', true).first().dom.innerHTML = period;
20040 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20042 var cls = ['bottom'];
20044 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20051 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20056 this.picker().addClass(cls.join('-'));
20060 Roo.each(cls, function(c){
20062 _this.picker().setTop(_this.inputEl().getHeight());
20066 _this.picker().setTop(0 - _this.picker().getHeight());
20071 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20075 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20082 onFocus : function()
20084 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20088 onBlur : function()
20090 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20096 this.picker().show();
20101 this.fireEvent('show', this, this.date);
20106 this.picker().hide();
20109 this.fireEvent('hide', this, this.date);
20112 setTime : function()
20115 this.setValue(this.time.format(this.format));
20117 this.fireEvent('select', this, this.date);
20122 onMousedown: function(e){
20123 e.stopPropagation();
20124 e.preventDefault();
20127 onIncrementHours: function()
20129 Roo.log('onIncrementHours');
20130 this.time = this.time.add(Date.HOUR, 1);
20135 onDecrementHours: function()
20137 Roo.log('onDecrementHours');
20138 this.time = this.time.add(Date.HOUR, -1);
20142 onIncrementMinutes: function()
20144 Roo.log('onIncrementMinutes');
20145 this.time = this.time.add(Date.MINUTE, 1);
20149 onDecrementMinutes: function()
20151 Roo.log('onDecrementMinutes');
20152 this.time = this.time.add(Date.MINUTE, -1);
20156 onTogglePeriod: function()
20158 Roo.log('onTogglePeriod');
20159 this.time = this.time.add(Date.HOUR, 12);
20166 Roo.apply(Roo.bootstrap.TimeField, {
20196 cls: 'btn btn-info ok',
20208 Roo.apply(Roo.bootstrap.TimeField, {
20212 cls: 'datepicker dropdown-menu',
20216 cls: 'datepicker-time',
20220 cls: 'table-condensed',
20222 Roo.bootstrap.TimeField.content,
20223 Roo.bootstrap.TimeField.footer
20242 * @class Roo.bootstrap.MonthField
20243 * @extends Roo.bootstrap.Input
20244 * Bootstrap MonthField class
20246 * @cfg {String} language default en
20249 * Create a new MonthField
20250 * @param {Object} config The config object
20253 Roo.bootstrap.MonthField = function(config){
20254 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20259 * Fires when this field show.
20260 * @param {Roo.bootstrap.MonthField} this
20261 * @param {Mixed} date The date value
20266 * Fires when this field hide.
20267 * @param {Roo.bootstrap.MonthField} this
20268 * @param {Mixed} date The date value
20273 * Fires when select a date.
20274 * @param {Roo.bootstrap.MonthField} this
20275 * @param {String} oldvalue The old value
20276 * @param {String} newvalue The new value
20282 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20284 onRender: function(ct, position)
20287 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20289 this.language = this.language || 'en';
20290 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20291 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20293 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20294 this.isInline = false;
20295 this.isInput = true;
20296 this.component = this.el.select('.add-on', true).first() || false;
20297 this.component = (this.component && this.component.length === 0) ? false : this.component;
20298 this.hasInput = this.component && this.inputEL().length;
20300 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20302 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20304 this.picker().on('mousedown', this.onMousedown, this);
20305 this.picker().on('click', this.onClick, this);
20307 this.picker().addClass('datepicker-dropdown');
20309 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20310 v.setStyle('width', '189px');
20317 if(this.isInline) {
20323 setValue: function(v, suppressEvent)
20325 var o = this.getValue();
20327 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20331 if(suppressEvent !== true){
20332 this.fireEvent('select', this, o, v);
20337 getValue: function()
20342 onClick: function(e)
20344 e.stopPropagation();
20345 e.preventDefault();
20347 var target = e.getTarget();
20349 if(target.nodeName.toLowerCase() === 'i'){
20350 target = Roo.get(target).dom.parentNode;
20353 var nodeName = target.nodeName;
20354 var className = target.className;
20355 var html = target.innerHTML;
20357 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20361 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20363 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20369 picker : function()
20371 return this.pickerEl;
20374 fillMonths: function()
20377 var months = this.picker().select('>.datepicker-months td', true).first();
20379 months.dom.innerHTML = '';
20385 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20388 months.createChild(month);
20397 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20398 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20401 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20402 e.removeClass('active');
20404 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20405 e.addClass('active');
20412 if(this.isInline) {
20416 this.picker().removeClass(['bottom', 'top']);
20418 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20420 * place to the top of element!
20424 this.picker().addClass('top');
20425 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20430 this.picker().addClass('bottom');
20432 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20435 onFocus : function()
20437 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20441 onBlur : function()
20443 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20445 var d = this.inputEl().getValue();
20454 this.picker().show();
20455 this.picker().select('>.datepicker-months', true).first().show();
20459 this.fireEvent('show', this, this.date);
20464 if(this.isInline) {
20467 this.picker().hide();
20468 this.fireEvent('hide', this, this.date);
20472 onMousedown: function(e)
20474 e.stopPropagation();
20475 e.preventDefault();
20480 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20484 fireKey: function(e)
20486 if (!this.picker().isVisible()){
20487 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20498 e.preventDefault();
20502 dir = e.keyCode == 37 ? -1 : 1;
20504 this.vIndex = this.vIndex + dir;
20506 if(this.vIndex < 0){
20510 if(this.vIndex > 11){
20514 if(isNaN(this.vIndex)){
20518 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20524 dir = e.keyCode == 38 ? -1 : 1;
20526 this.vIndex = this.vIndex + dir * 4;
20528 if(this.vIndex < 0){
20532 if(this.vIndex > 11){
20536 if(isNaN(this.vIndex)){
20540 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20545 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20546 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20550 e.preventDefault();
20553 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20554 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20570 this.picker().remove();
20575 Roo.apply(Roo.bootstrap.MonthField, {
20594 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20595 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20600 Roo.apply(Roo.bootstrap.MonthField, {
20604 cls: 'datepicker dropdown-menu roo-dynamic',
20608 cls: 'datepicker-months',
20612 cls: 'table-condensed',
20614 Roo.bootstrap.DateField.content
20634 * @class Roo.bootstrap.CheckBox
20635 * @extends Roo.bootstrap.Input
20636 * Bootstrap CheckBox class
20638 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20639 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20640 * @cfg {String} boxLabel The text that appears beside the checkbox
20641 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20642 * @cfg {Boolean} checked initnal the element
20643 * @cfg {Boolean} inline inline the element (default false)
20644 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20645 * @cfg {String} tooltip label tooltip
20648 * Create a new CheckBox
20649 * @param {Object} config The config object
20652 Roo.bootstrap.CheckBox = function(config){
20653 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20658 * Fires when the element is checked or unchecked.
20659 * @param {Roo.bootstrap.CheckBox} this This input
20660 * @param {Boolean} checked The new checked value
20665 * Fires when the element is click.
20666 * @param {Roo.bootstrap.CheckBox} this This input
20673 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20675 inputType: 'checkbox',
20684 getAutoCreate : function()
20686 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20692 cfg.cls = 'form-group ' + this.inputType; //input-group
20695 cfg.cls += ' ' + this.inputType + '-inline';
20701 type : this.inputType,
20702 value : this.inputValue,
20703 cls : 'roo-' + this.inputType, //'form-box',
20704 placeholder : this.placeholder || ''
20708 if(this.inputType != 'radio'){
20712 cls : 'roo-hidden-value',
20713 value : this.checked ? this.inputValue : this.valueOff
20718 if (this.weight) { // Validity check?
20719 cfg.cls += " " + this.inputType + "-" + this.weight;
20722 if (this.disabled) {
20723 input.disabled=true;
20727 input.checked = this.checked;
20732 input.name = this.name;
20734 if(this.inputType != 'radio'){
20735 hidden.name = this.name;
20736 input.name = '_hidden_' + this.name;
20741 input.cls += ' input-' + this.size;
20746 ['xs','sm','md','lg'].map(function(size){
20747 if (settings[size]) {
20748 cfg.cls += ' col-' + size + '-' + settings[size];
20752 var inputblock = input;
20754 if (this.before || this.after) {
20757 cls : 'input-group',
20762 inputblock.cn.push({
20764 cls : 'input-group-addon',
20769 inputblock.cn.push(input);
20771 if(this.inputType != 'radio'){
20772 inputblock.cn.push(hidden);
20776 inputblock.cn.push({
20778 cls : 'input-group-addon',
20785 if (align ==='left' && this.fieldLabel.length) {
20786 // Roo.log("left and has label");
20791 cls : 'control-label',
20792 html : this.fieldLabel
20802 if(this.labelWidth > 12){
20803 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20806 if(this.labelWidth < 13 && this.labelmd == 0){
20807 this.labelmd = this.labelWidth;
20810 if(this.labellg > 0){
20811 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20812 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20815 if(this.labelmd > 0){
20816 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20817 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20820 if(this.labelsm > 0){
20821 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20822 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20825 if(this.labelxs > 0){
20826 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20827 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20830 } else if ( this.fieldLabel.length) {
20831 // Roo.log(" label");
20835 tag: this.boxLabel ? 'span' : 'label',
20837 cls: 'control-label box-input-label',
20838 //cls : 'input-group-addon',
20839 html : this.fieldLabel
20848 // Roo.log(" no label && no align");
20849 cfg.cn = [ inputblock ] ;
20855 var boxLabelCfg = {
20857 //'for': id, // box label is handled by onclick - so no for...
20859 html: this.boxLabel
20863 boxLabelCfg.tooltip = this.tooltip;
20866 cfg.cn.push(boxLabelCfg);
20869 if(this.inputType != 'radio'){
20870 cfg.cn.push(hidden);
20878 * return the real input element.
20880 inputEl: function ()
20882 return this.el.select('input.roo-' + this.inputType,true).first();
20884 hiddenEl: function ()
20886 return this.el.select('input.roo-hidden-value',true).first();
20889 labelEl: function()
20891 return this.el.select('label.control-label',true).first();
20893 /* depricated... */
20897 return this.labelEl();
20900 boxLabelEl: function()
20902 return this.el.select('label.box-label',true).first();
20905 initEvents : function()
20907 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20909 this.inputEl().on('click', this.onClick, this);
20911 if (this.boxLabel) {
20912 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20915 this.startValue = this.getValue();
20918 Roo.bootstrap.CheckBox.register(this);
20922 onClick : function(e)
20924 if(this.fireEvent('click', this, e) !== false){
20925 this.setChecked(!this.checked);
20930 setChecked : function(state,suppressEvent)
20932 this.startValue = this.getValue();
20934 if(this.inputType == 'radio'){
20936 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20937 e.dom.checked = false;
20940 this.inputEl().dom.checked = true;
20942 this.inputEl().dom.value = this.inputValue;
20944 if(suppressEvent !== true){
20945 this.fireEvent('check', this, true);
20953 this.checked = state;
20955 this.inputEl().dom.checked = state;
20958 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20960 if(suppressEvent !== true){
20961 this.fireEvent('check', this, state);
20967 getValue : function()
20969 if(this.inputType == 'radio'){
20970 return this.getGroupValue();
20973 return this.hiddenEl().dom.value;
20977 getGroupValue : function()
20979 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20983 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20986 setValue : function(v,suppressEvent)
20988 if(this.inputType == 'radio'){
20989 this.setGroupValue(v, suppressEvent);
20993 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20998 setGroupValue : function(v, suppressEvent)
21000 this.startValue = this.getValue();
21002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21003 e.dom.checked = false;
21005 if(e.dom.value == v){
21006 e.dom.checked = true;
21010 if(suppressEvent !== true){
21011 this.fireEvent('check', this, true);
21019 validate : function()
21021 if(this.getVisibilityEl().hasClass('hidden')){
21027 (this.inputType == 'radio' && this.validateRadio()) ||
21028 (this.inputType == 'checkbox' && this.validateCheckbox())
21034 this.markInvalid();
21038 validateRadio : function()
21040 if(this.getVisibilityEl().hasClass('hidden')){
21044 if(this.allowBlank){
21050 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21051 if(!e.dom.checked){
21063 validateCheckbox : function()
21066 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21067 //return (this.getValue() == this.inputValue) ? true : false;
21070 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21078 for(var i in group){
21079 if(group[i].el.isVisible(true)){
21087 for(var i in group){
21092 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21099 * Mark this field as valid
21101 markValid : function()
21105 this.fireEvent('valid', this);
21107 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21110 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21117 if(this.inputType == 'radio'){
21118 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21119 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21120 e.findParent('.form-group', false, true).addClass(_this.validClass);
21127 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21128 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21132 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21138 for(var i in group){
21139 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21140 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21145 * Mark this field as invalid
21146 * @param {String} msg The validation message
21148 markInvalid : function(msg)
21150 if(this.allowBlank){
21156 this.fireEvent('invalid', this, msg);
21158 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21161 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21165 label.markInvalid();
21168 if(this.inputType == 'radio'){
21169 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21170 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21171 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21178 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21179 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21183 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21189 for(var i in group){
21190 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21191 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21196 clearInvalid : function()
21198 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21200 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21202 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21204 if (label && label.iconEl) {
21205 label.iconEl.removeClass(label.validClass);
21206 label.iconEl.removeClass(label.invalidClass);
21210 disable : function()
21212 if(this.inputType != 'radio'){
21213 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21220 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21221 _this.getActionEl().addClass(this.disabledClass);
21222 e.dom.disabled = true;
21226 this.disabled = true;
21227 this.fireEvent("disable", this);
21231 enable : function()
21233 if(this.inputType != 'radio'){
21234 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21241 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21242 _this.getActionEl().removeClass(this.disabledClass);
21243 e.dom.disabled = false;
21247 this.disabled = false;
21248 this.fireEvent("enable", this);
21252 setBoxLabel : function(v)
21257 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21263 Roo.apply(Roo.bootstrap.CheckBox, {
21268 * register a CheckBox Group
21269 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21271 register : function(checkbox)
21273 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21274 this.groups[checkbox.groupId] = {};
21277 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21281 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21285 * fetch a CheckBox Group based on the group ID
21286 * @param {string} the group ID
21287 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21289 get: function(groupId) {
21290 if (typeof(this.groups[groupId]) == 'undefined') {
21294 return this.groups[groupId] ;
21307 * @class Roo.bootstrap.Radio
21308 * @extends Roo.bootstrap.Component
21309 * Bootstrap Radio class
21310 * @cfg {String} boxLabel - the label associated
21311 * @cfg {String} value - the value of radio
21314 * Create a new Radio
21315 * @param {Object} config The config object
21317 Roo.bootstrap.Radio = function(config){
21318 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21322 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21328 getAutoCreate : function()
21332 cls : 'form-group radio',
21337 html : this.boxLabel
21345 initEvents : function()
21347 this.parent().register(this);
21349 this.el.on('click', this.onClick, this);
21353 onClick : function(e)
21355 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21356 this.setChecked(true);
21360 setChecked : function(state, suppressEvent)
21362 this.parent().setValue(this.value, suppressEvent);
21366 setBoxLabel : function(v)
21371 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21386 * @class Roo.bootstrap.SecurePass
21387 * @extends Roo.bootstrap.Input
21388 * Bootstrap SecurePass class
21392 * Create a new SecurePass
21393 * @param {Object} config The config object
21396 Roo.bootstrap.SecurePass = function (config) {
21397 // these go here, so the translation tool can replace them..
21399 PwdEmpty: "Please type a password, and then retype it to confirm.",
21400 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21401 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21402 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21403 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21404 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21405 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21406 TooWeak: "Your password is Too Weak."
21408 this.meterLabel = "Password strength:";
21409 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21410 this.meterClass = [
21411 "roo-password-meter-tooweak",
21412 "roo-password-meter-weak",
21413 "roo-password-meter-medium",
21414 "roo-password-meter-strong",
21415 "roo-password-meter-grey"
21420 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21423 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21425 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21427 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21428 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21429 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21430 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21431 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21432 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21433 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21443 * @cfg {String/Object} Label for the strength meter (defaults to
21444 * 'Password strength:')
21449 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21450 * ['Weak', 'Medium', 'Strong'])
21453 pwdStrengths: false,
21466 initEvents: function ()
21468 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21470 if (this.el.is('input[type=password]') && Roo.isSafari) {
21471 this.el.on('keydown', this.SafariOnKeyDown, this);
21474 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21477 onRender: function (ct, position)
21479 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21480 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21481 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21483 this.trigger.createChild({
21488 cls: 'roo-password-meter-grey col-xs-12',
21491 //width: this.meterWidth + 'px'
21495 cls: 'roo-password-meter-text'
21501 if (this.hideTrigger) {
21502 this.trigger.setDisplayed(false);
21504 this.setSize(this.width || '', this.height || '');
21507 onDestroy: function ()
21509 if (this.trigger) {
21510 this.trigger.removeAllListeners();
21511 this.trigger.remove();
21514 this.wrap.remove();
21516 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21519 checkStrength: function ()
21521 var pwd = this.inputEl().getValue();
21522 if (pwd == this._lastPwd) {
21527 if (this.ClientSideStrongPassword(pwd)) {
21529 } else if (this.ClientSideMediumPassword(pwd)) {
21531 } else if (this.ClientSideWeakPassword(pwd)) {
21537 Roo.log('strength1: ' + strength);
21539 //var pm = this.trigger.child('div/div/div').dom;
21540 var pm = this.trigger.child('div/div');
21541 pm.removeClass(this.meterClass);
21542 pm.addClass(this.meterClass[strength]);
21545 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21547 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21549 this._lastPwd = pwd;
21553 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21555 this._lastPwd = '';
21557 var pm = this.trigger.child('div/div');
21558 pm.removeClass(this.meterClass);
21559 pm.addClass('roo-password-meter-grey');
21562 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21565 this.inputEl().dom.type='password';
21568 validateValue: function (value)
21571 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21574 if (value.length == 0) {
21575 if (this.allowBlank) {
21576 this.clearInvalid();
21580 this.markInvalid(this.errors.PwdEmpty);
21581 this.errorMsg = this.errors.PwdEmpty;
21589 if ('[\x21-\x7e]*'.match(value)) {
21590 this.markInvalid(this.errors.PwdBadChar);
21591 this.errorMsg = this.errors.PwdBadChar;
21594 if (value.length < 6) {
21595 this.markInvalid(this.errors.PwdShort);
21596 this.errorMsg = this.errors.PwdShort;
21599 if (value.length > 16) {
21600 this.markInvalid(this.errors.PwdLong);
21601 this.errorMsg = this.errors.PwdLong;
21605 if (this.ClientSideStrongPassword(value)) {
21607 } else if (this.ClientSideMediumPassword(value)) {
21609 } else if (this.ClientSideWeakPassword(value)) {
21616 if (strength < 2) {
21617 //this.markInvalid(this.errors.TooWeak);
21618 this.errorMsg = this.errors.TooWeak;
21623 console.log('strength2: ' + strength);
21625 //var pm = this.trigger.child('div/div/div').dom;
21627 var pm = this.trigger.child('div/div');
21628 pm.removeClass(this.meterClass);
21629 pm.addClass(this.meterClass[strength]);
21631 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21633 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21635 this.errorMsg = '';
21639 CharacterSetChecks: function (type)
21642 this.fResult = false;
21645 isctype: function (character, type)
21648 case this.kCapitalLetter:
21649 if (character >= 'A' && character <= 'Z') {
21654 case this.kSmallLetter:
21655 if (character >= 'a' && character <= 'z') {
21661 if (character >= '0' && character <= '9') {
21666 case this.kPunctuation:
21667 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21678 IsLongEnough: function (pwd, size)
21680 return !(pwd == null || isNaN(size) || pwd.length < size);
21683 SpansEnoughCharacterSets: function (word, nb)
21685 if (!this.IsLongEnough(word, nb))
21690 var characterSetChecks = new Array(
21691 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21692 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21695 for (var index = 0; index < word.length; ++index) {
21696 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21697 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21698 characterSetChecks[nCharSet].fResult = true;
21705 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21706 if (characterSetChecks[nCharSet].fResult) {
21711 if (nCharSets < nb) {
21717 ClientSideStrongPassword: function (pwd)
21719 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21722 ClientSideMediumPassword: function (pwd)
21724 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21727 ClientSideWeakPassword: function (pwd)
21729 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21732 })//<script type="text/javascript">
21735 * Based Ext JS Library 1.1.1
21736 * Copyright(c) 2006-2007, Ext JS, LLC.
21742 * @class Roo.HtmlEditorCore
21743 * @extends Roo.Component
21744 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21746 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21749 Roo.HtmlEditorCore = function(config){
21752 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21757 * @event initialize
21758 * Fires when the editor is fully initialized (including the iframe)
21759 * @param {Roo.HtmlEditorCore} this
21764 * Fires when the editor is first receives the focus. Any insertion must wait
21765 * until after this event.
21766 * @param {Roo.HtmlEditorCore} this
21770 * @event beforesync
21771 * Fires before the textarea is updated with content from the editor iframe. Return false
21772 * to cancel the sync.
21773 * @param {Roo.HtmlEditorCore} this
21774 * @param {String} html
21778 * @event beforepush
21779 * Fires before the iframe editor is updated with content from the textarea. Return false
21780 * to cancel the push.
21781 * @param {Roo.HtmlEditorCore} this
21782 * @param {String} html
21787 * Fires when the textarea is updated with content from the editor iframe.
21788 * @param {Roo.HtmlEditorCore} this
21789 * @param {String} html
21794 * Fires when the iframe editor is updated with content from the textarea.
21795 * @param {Roo.HtmlEditorCore} this
21796 * @param {String} html
21801 * @event editorevent
21802 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21803 * @param {Roo.HtmlEditorCore} this
21809 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21811 // defaults : white / black...
21812 this.applyBlacklists();
21819 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21823 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21829 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21834 * @cfg {Number} height (in pixels)
21838 * @cfg {Number} width (in pixels)
21843 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21846 stylesheets: false,
21851 // private properties
21852 validationEvent : false,
21854 initialized : false,
21856 sourceEditMode : false,
21857 onFocus : Roo.emptyFn,
21859 hideMode:'offsets',
21863 // blacklist + whitelisted elements..
21870 * Protected method that will not generally be called directly. It
21871 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21872 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21874 getDocMarkup : function(){
21878 // inherit styels from page...??
21879 if (this.stylesheets === false) {
21881 Roo.get(document.head).select('style').each(function(node) {
21882 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21885 Roo.get(document.head).select('link').each(function(node) {
21886 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21889 } else if (!this.stylesheets.length) {
21891 st = '<style type="text/css">' +
21892 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21895 st = '<style type="text/css">' +
21900 st += '<style type="text/css">' +
21901 'IMG { cursor: pointer } ' +
21904 var cls = 'roo-htmleditor-body';
21906 if(this.bodyCls.length){
21907 cls += ' ' + this.bodyCls;
21910 return '<html><head>' + st +
21911 //<style type="text/css">' +
21912 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21914 ' </head><body class="' + cls + '"></body></html>';
21918 onRender : function(ct, position)
21921 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21922 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21925 this.el.dom.style.border = '0 none';
21926 this.el.dom.setAttribute('tabIndex', -1);
21927 this.el.addClass('x-hidden hide');
21931 if(Roo.isIE){ // fix IE 1px bogus margin
21932 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21936 this.frameId = Roo.id();
21940 var iframe = this.owner.wrap.createChild({
21942 cls: 'form-control', // bootstrap..
21944 name: this.frameId,
21945 frameBorder : 'no',
21946 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21951 this.iframe = iframe.dom;
21953 this.assignDocWin();
21955 this.doc.designMode = 'on';
21958 this.doc.write(this.getDocMarkup());
21962 var task = { // must defer to wait for browser to be ready
21964 //console.log("run task?" + this.doc.readyState);
21965 this.assignDocWin();
21966 if(this.doc.body || this.doc.readyState == 'complete'){
21968 this.doc.designMode="on";
21972 Roo.TaskMgr.stop(task);
21973 this.initEditor.defer(10, this);
21980 Roo.TaskMgr.start(task);
21985 onResize : function(w, h)
21987 Roo.log('resize: ' +w + ',' + h );
21988 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21992 if(typeof w == 'number'){
21994 this.iframe.style.width = w + 'px';
21996 if(typeof h == 'number'){
21998 this.iframe.style.height = h + 'px';
22000 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22007 * Toggles the editor between standard and source edit mode.
22008 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22010 toggleSourceEdit : function(sourceEditMode){
22012 this.sourceEditMode = sourceEditMode === true;
22014 if(this.sourceEditMode){
22016 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22019 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22020 //this.iframe.className = '';
22023 //this.setSize(this.owner.wrap.getSize());
22024 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22031 * Protected method that will not generally be called directly. If you need/want
22032 * custom HTML cleanup, this is the method you should override.
22033 * @param {String} html The HTML to be cleaned
22034 * return {String} The cleaned HTML
22036 cleanHtml : function(html){
22037 html = String(html);
22038 if(html.length > 5){
22039 if(Roo.isSafari){ // strip safari nonsense
22040 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22043 if(html == ' '){
22050 * HTML Editor -> Textarea
22051 * Protected method that will not generally be called directly. Syncs the contents
22052 * of the editor iframe with the textarea.
22054 syncValue : function(){
22055 if(this.initialized){
22056 var bd = (this.doc.body || this.doc.documentElement);
22057 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22058 var html = bd.innerHTML;
22060 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22061 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22063 html = '<div style="'+m[0]+'">' + html + '</div>';
22066 html = this.cleanHtml(html);
22067 // fix up the special chars.. normaly like back quotes in word...
22068 // however we do not want to do this with chinese..
22069 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22070 var cc = b.charCodeAt();
22072 (cc >= 0x4E00 && cc < 0xA000 ) ||
22073 (cc >= 0x3400 && cc < 0x4E00 ) ||
22074 (cc >= 0xf900 && cc < 0xfb00 )
22080 if(this.owner.fireEvent('beforesync', this, html) !== false){
22081 this.el.dom.value = html;
22082 this.owner.fireEvent('sync', this, html);
22088 * Protected method that will not generally be called directly. Pushes the value of the textarea
22089 * into the iframe editor.
22091 pushValue : function(){
22092 if(this.initialized){
22093 var v = this.el.dom.value.trim();
22095 // if(v.length < 1){
22099 if(this.owner.fireEvent('beforepush', this, v) !== false){
22100 var d = (this.doc.body || this.doc.documentElement);
22102 this.cleanUpPaste();
22103 this.el.dom.value = d.innerHTML;
22104 this.owner.fireEvent('push', this, v);
22110 deferFocus : function(){
22111 this.focus.defer(10, this);
22115 focus : function(){
22116 if(this.win && !this.sourceEditMode){
22123 assignDocWin: function()
22125 var iframe = this.iframe;
22128 this.doc = iframe.contentWindow.document;
22129 this.win = iframe.contentWindow;
22131 // if (!Roo.get(this.frameId)) {
22134 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22135 // this.win = Roo.get(this.frameId).dom.contentWindow;
22137 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22141 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22142 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22147 initEditor : function(){
22148 //console.log("INIT EDITOR");
22149 this.assignDocWin();
22153 this.doc.designMode="on";
22155 this.doc.write(this.getDocMarkup());
22158 var dbody = (this.doc.body || this.doc.documentElement);
22159 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22160 // this copies styles from the containing element into thsi one..
22161 // not sure why we need all of this..
22162 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22164 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22165 //ss['background-attachment'] = 'fixed'; // w3c
22166 dbody.bgProperties = 'fixed'; // ie
22167 //Roo.DomHelper.applyStyles(dbody, ss);
22168 Roo.EventManager.on(this.doc, {
22169 //'mousedown': this.onEditorEvent,
22170 'mouseup': this.onEditorEvent,
22171 'dblclick': this.onEditorEvent,
22172 'click': this.onEditorEvent,
22173 'keyup': this.onEditorEvent,
22178 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22180 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22181 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22183 this.initialized = true;
22185 this.owner.fireEvent('initialize', this);
22190 onDestroy : function(){
22196 //for (var i =0; i < this.toolbars.length;i++) {
22197 // // fixme - ask toolbars for heights?
22198 // this.toolbars[i].onDestroy();
22201 //this.wrap.dom.innerHTML = '';
22202 //this.wrap.remove();
22207 onFirstFocus : function(){
22209 this.assignDocWin();
22212 this.activated = true;
22215 if(Roo.isGecko){ // prevent silly gecko errors
22217 var s = this.win.getSelection();
22218 if(!s.focusNode || s.focusNode.nodeType != 3){
22219 var r = s.getRangeAt(0);
22220 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22225 this.execCmd('useCSS', true);
22226 this.execCmd('styleWithCSS', false);
22229 this.owner.fireEvent('activate', this);
22233 adjustFont: function(btn){
22234 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22235 //if(Roo.isSafari){ // safari
22238 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22239 if(Roo.isSafari){ // safari
22240 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22241 v = (v < 10) ? 10 : v;
22242 v = (v > 48) ? 48 : v;
22243 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22248 v = Math.max(1, v+adjust);
22250 this.execCmd('FontSize', v );
22253 onEditorEvent : function(e)
22255 this.owner.fireEvent('editorevent', this, e);
22256 // this.updateToolbar();
22257 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22260 insertTag : function(tg)
22262 // could be a bit smarter... -> wrap the current selected tRoo..
22263 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22265 range = this.createRange(this.getSelection());
22266 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22267 wrappingNode.appendChild(range.extractContents());
22268 range.insertNode(wrappingNode);
22275 this.execCmd("formatblock", tg);
22279 insertText : function(txt)
22283 var range = this.createRange();
22284 range.deleteContents();
22285 //alert(Sender.getAttribute('label'));
22287 range.insertNode(this.doc.createTextNode(txt));
22293 * Executes a Midas editor command on the editor document and performs necessary focus and
22294 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22295 * @param {String} cmd The Midas command
22296 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22298 relayCmd : function(cmd, value){
22300 this.execCmd(cmd, value);
22301 this.owner.fireEvent('editorevent', this);
22302 //this.updateToolbar();
22303 this.owner.deferFocus();
22307 * Executes a Midas editor command directly on the editor document.
22308 * For visual commands, you should use {@link #relayCmd} instead.
22309 * <b>This should only be called after the editor is initialized.</b>
22310 * @param {String} cmd The Midas command
22311 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22313 execCmd : function(cmd, value){
22314 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22321 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22323 * @param {String} text | dom node..
22325 insertAtCursor : function(text)
22328 if(!this.activated){
22334 var r = this.doc.selection.createRange();
22345 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22349 // from jquery ui (MIT licenced)
22351 var win = this.win;
22353 if (win.getSelection && win.getSelection().getRangeAt) {
22354 range = win.getSelection().getRangeAt(0);
22355 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22356 range.insertNode(node);
22357 } else if (win.document.selection && win.document.selection.createRange) {
22358 // no firefox support
22359 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22360 win.document.selection.createRange().pasteHTML(txt);
22362 // no firefox support
22363 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22364 this.execCmd('InsertHTML', txt);
22373 mozKeyPress : function(e){
22375 var c = e.getCharCode(), cmd;
22378 c = String.fromCharCode(c).toLowerCase();
22392 this.cleanUpPaste.defer(100, this);
22400 e.preventDefault();
22408 fixKeys : function(){ // load time branching for fastest keydown performance
22410 return function(e){
22411 var k = e.getKey(), r;
22414 r = this.doc.selection.createRange();
22417 r.pasteHTML('    ');
22424 r = this.doc.selection.createRange();
22426 var target = r.parentElement();
22427 if(!target || target.tagName.toLowerCase() != 'li'){
22429 r.pasteHTML('<br />');
22435 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22436 this.cleanUpPaste.defer(100, this);
22442 }else if(Roo.isOpera){
22443 return function(e){
22444 var k = e.getKey();
22448 this.execCmd('InsertHTML','    ');
22451 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22452 this.cleanUpPaste.defer(100, this);
22457 }else if(Roo.isSafari){
22458 return function(e){
22459 var k = e.getKey();
22463 this.execCmd('InsertText','\t');
22467 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22468 this.cleanUpPaste.defer(100, this);
22476 getAllAncestors: function()
22478 var p = this.getSelectedNode();
22481 a.push(p); // push blank onto stack..
22482 p = this.getParentElement();
22486 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22490 a.push(this.doc.body);
22494 lastSelNode : false,
22497 getSelection : function()
22499 this.assignDocWin();
22500 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22503 getSelectedNode: function()
22505 // this may only work on Gecko!!!
22507 // should we cache this!!!!
22512 var range = this.createRange(this.getSelection()).cloneRange();
22515 var parent = range.parentElement();
22517 var testRange = range.duplicate();
22518 testRange.moveToElementText(parent);
22519 if (testRange.inRange(range)) {
22522 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22525 parent = parent.parentElement;
22530 // is ancestor a text element.
22531 var ac = range.commonAncestorContainer;
22532 if (ac.nodeType == 3) {
22533 ac = ac.parentNode;
22536 var ar = ac.childNodes;
22539 var other_nodes = [];
22540 var has_other_nodes = false;
22541 for (var i=0;i<ar.length;i++) {
22542 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22545 // fullly contained node.
22547 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22552 // probably selected..
22553 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22554 other_nodes.push(ar[i]);
22558 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22563 has_other_nodes = true;
22565 if (!nodes.length && other_nodes.length) {
22566 nodes= other_nodes;
22568 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22574 createRange: function(sel)
22576 // this has strange effects when using with
22577 // top toolbar - not sure if it's a great idea.
22578 //this.editor.contentWindow.focus();
22579 if (typeof sel != "undefined") {
22581 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22583 return this.doc.createRange();
22586 return this.doc.createRange();
22589 getParentElement: function()
22592 this.assignDocWin();
22593 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22595 var range = this.createRange(sel);
22598 var p = range.commonAncestorContainer;
22599 while (p.nodeType == 3) { // text node
22610 * Range intersection.. the hard stuff...
22614 * [ -- selected range --- ]
22618 * if end is before start or hits it. fail.
22619 * if start is after end or hits it fail.
22621 * if either hits (but other is outside. - then it's not
22627 // @see http://www.thismuchiknow.co.uk/?p=64.
22628 rangeIntersectsNode : function(range, node)
22630 var nodeRange = node.ownerDocument.createRange();
22632 nodeRange.selectNode(node);
22634 nodeRange.selectNodeContents(node);
22637 var rangeStartRange = range.cloneRange();
22638 rangeStartRange.collapse(true);
22640 var rangeEndRange = range.cloneRange();
22641 rangeEndRange.collapse(false);
22643 var nodeStartRange = nodeRange.cloneRange();
22644 nodeStartRange.collapse(true);
22646 var nodeEndRange = nodeRange.cloneRange();
22647 nodeEndRange.collapse(false);
22649 return rangeStartRange.compareBoundaryPoints(
22650 Range.START_TO_START, nodeEndRange) == -1 &&
22651 rangeEndRange.compareBoundaryPoints(
22652 Range.START_TO_START, nodeStartRange) == 1;
22656 rangeCompareNode : function(range, node)
22658 var nodeRange = node.ownerDocument.createRange();
22660 nodeRange.selectNode(node);
22662 nodeRange.selectNodeContents(node);
22666 range.collapse(true);
22668 nodeRange.collapse(true);
22670 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22671 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22673 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22675 var nodeIsBefore = ss == 1;
22676 var nodeIsAfter = ee == -1;
22678 if (nodeIsBefore && nodeIsAfter) {
22681 if (!nodeIsBefore && nodeIsAfter) {
22682 return 1; //right trailed.
22685 if (nodeIsBefore && !nodeIsAfter) {
22686 return 2; // left trailed.
22692 // private? - in a new class?
22693 cleanUpPaste : function()
22695 // cleans up the whole document..
22696 Roo.log('cleanuppaste');
22698 this.cleanUpChildren(this.doc.body);
22699 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22700 if (clean != this.doc.body.innerHTML) {
22701 this.doc.body.innerHTML = clean;
22706 cleanWordChars : function(input) {// change the chars to hex code
22707 var he = Roo.HtmlEditorCore;
22709 var output = input;
22710 Roo.each(he.swapCodes, function(sw) {
22711 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22713 output = output.replace(swapper, sw[1]);
22720 cleanUpChildren : function (n)
22722 if (!n.childNodes.length) {
22725 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22726 this.cleanUpChild(n.childNodes[i]);
22733 cleanUpChild : function (node)
22736 //console.log(node);
22737 if (node.nodeName == "#text") {
22738 // clean up silly Windows -- stuff?
22741 if (node.nodeName == "#comment") {
22742 node.parentNode.removeChild(node);
22743 // clean up silly Windows -- stuff?
22746 var lcname = node.tagName.toLowerCase();
22747 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22748 // whitelist of tags..
22750 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22752 node.parentNode.removeChild(node);
22757 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22759 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22760 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22762 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22763 // remove_keep_children = true;
22766 if (remove_keep_children) {
22767 this.cleanUpChildren(node);
22768 // inserts everything just before this node...
22769 while (node.childNodes.length) {
22770 var cn = node.childNodes[0];
22771 node.removeChild(cn);
22772 node.parentNode.insertBefore(cn, node);
22774 node.parentNode.removeChild(node);
22778 if (!node.attributes || !node.attributes.length) {
22779 this.cleanUpChildren(node);
22783 function cleanAttr(n,v)
22786 if (v.match(/^\./) || v.match(/^\//)) {
22789 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22792 if (v.match(/^#/)) {
22795 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22796 node.removeAttribute(n);
22800 var cwhite = this.cwhite;
22801 var cblack = this.cblack;
22803 function cleanStyle(n,v)
22805 if (v.match(/expression/)) { //XSS?? should we even bother..
22806 node.removeAttribute(n);
22810 var parts = v.split(/;/);
22813 Roo.each(parts, function(p) {
22814 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22818 var l = p.split(':').shift().replace(/\s+/g,'');
22819 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22821 if ( cwhite.length && cblack.indexOf(l) > -1) {
22822 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22823 //node.removeAttribute(n);
22827 // only allow 'c whitelisted system attributes'
22828 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22829 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22830 //node.removeAttribute(n);
22840 if (clean.length) {
22841 node.setAttribute(n, clean.join(';'));
22843 node.removeAttribute(n);
22849 for (var i = node.attributes.length-1; i > -1 ; i--) {
22850 var a = node.attributes[i];
22853 if (a.name.toLowerCase().substr(0,2)=='on') {
22854 node.removeAttribute(a.name);
22857 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22858 node.removeAttribute(a.name);
22861 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22862 cleanAttr(a.name,a.value); // fixme..
22865 if (a.name == 'style') {
22866 cleanStyle(a.name,a.value);
22869 /// clean up MS crap..
22870 // tecnically this should be a list of valid class'es..
22873 if (a.name == 'class') {
22874 if (a.value.match(/^Mso/)) {
22875 node.className = '';
22878 if (a.value.match(/^body$/)) {
22879 node.className = '';
22890 this.cleanUpChildren(node);
22896 * Clean up MS wordisms...
22898 cleanWord : function(node)
22903 this.cleanWord(this.doc.body);
22906 if (node.nodeName == "#text") {
22907 // clean up silly Windows -- stuff?
22910 if (node.nodeName == "#comment") {
22911 node.parentNode.removeChild(node);
22912 // clean up silly Windows -- stuff?
22916 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22917 node.parentNode.removeChild(node);
22921 // remove - but keep children..
22922 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22923 while (node.childNodes.length) {
22924 var cn = node.childNodes[0];
22925 node.removeChild(cn);
22926 node.parentNode.insertBefore(cn, node);
22928 node.parentNode.removeChild(node);
22929 this.iterateChildren(node, this.cleanWord);
22933 if (node.className.length) {
22935 var cn = node.className.split(/\W+/);
22937 Roo.each(cn, function(cls) {
22938 if (cls.match(/Mso[a-zA-Z]+/)) {
22943 node.className = cna.length ? cna.join(' ') : '';
22945 node.removeAttribute("class");
22949 if (node.hasAttribute("lang")) {
22950 node.removeAttribute("lang");
22953 if (node.hasAttribute("style")) {
22955 var styles = node.getAttribute("style").split(";");
22957 Roo.each(styles, function(s) {
22958 if (!s.match(/:/)) {
22961 var kv = s.split(":");
22962 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22965 // what ever is left... we allow.
22968 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22969 if (!nstyle.length) {
22970 node.removeAttribute('style');
22973 this.iterateChildren(node, this.cleanWord);
22979 * iterateChildren of a Node, calling fn each time, using this as the scole..
22980 * @param {DomNode} node node to iterate children of.
22981 * @param {Function} fn method of this class to call on each item.
22983 iterateChildren : function(node, fn)
22985 if (!node.childNodes.length) {
22988 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22989 fn.call(this, node.childNodes[i])
22995 * cleanTableWidths.
22997 * Quite often pasting from word etc.. results in tables with column and widths.
22998 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23001 cleanTableWidths : function(node)
23006 this.cleanTableWidths(this.doc.body);
23011 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23014 Roo.log(node.tagName);
23015 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23016 this.iterateChildren(node, this.cleanTableWidths);
23019 if (node.hasAttribute('width')) {
23020 node.removeAttribute('width');
23024 if (node.hasAttribute("style")) {
23027 var styles = node.getAttribute("style").split(";");
23029 Roo.each(styles, function(s) {
23030 if (!s.match(/:/)) {
23033 var kv = s.split(":");
23034 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23037 // what ever is left... we allow.
23040 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23041 if (!nstyle.length) {
23042 node.removeAttribute('style');
23046 this.iterateChildren(node, this.cleanTableWidths);
23054 domToHTML : function(currentElement, depth, nopadtext) {
23056 depth = depth || 0;
23057 nopadtext = nopadtext || false;
23059 if (!currentElement) {
23060 return this.domToHTML(this.doc.body);
23063 //Roo.log(currentElement);
23065 var allText = false;
23066 var nodeName = currentElement.nodeName;
23067 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23069 if (nodeName == '#text') {
23071 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23076 if (nodeName != 'BODY') {
23079 // Prints the node tagName, such as <A>, <IMG>, etc
23082 for(i = 0; i < currentElement.attributes.length;i++) {
23084 var aname = currentElement.attributes.item(i).name;
23085 if (!currentElement.attributes.item(i).value.length) {
23088 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23091 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23100 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23103 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23108 // Traverse the tree
23110 var currentElementChild = currentElement.childNodes.item(i);
23111 var allText = true;
23112 var innerHTML = '';
23114 while (currentElementChild) {
23115 // Formatting code (indent the tree so it looks nice on the screen)
23116 var nopad = nopadtext;
23117 if (lastnode == 'SPAN') {
23121 if (currentElementChild.nodeName == '#text') {
23122 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23123 toadd = nopadtext ? toadd : toadd.trim();
23124 if (!nopad && toadd.length > 80) {
23125 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23127 innerHTML += toadd;
23130 currentElementChild = currentElement.childNodes.item(i);
23136 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23138 // Recursively traverse the tree structure of the child node
23139 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23140 lastnode = currentElementChild.nodeName;
23142 currentElementChild=currentElement.childNodes.item(i);
23148 // The remaining code is mostly for formatting the tree
23149 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23154 ret+= "</"+tagName+">";
23160 applyBlacklists : function()
23162 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23163 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23167 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23168 if (b.indexOf(tag) > -1) {
23171 this.white.push(tag);
23175 Roo.each(w, function(tag) {
23176 if (b.indexOf(tag) > -1) {
23179 if (this.white.indexOf(tag) > -1) {
23182 this.white.push(tag);
23187 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23188 if (w.indexOf(tag) > -1) {
23191 this.black.push(tag);
23195 Roo.each(b, function(tag) {
23196 if (w.indexOf(tag) > -1) {
23199 if (this.black.indexOf(tag) > -1) {
23202 this.black.push(tag);
23207 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23208 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23212 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23213 if (b.indexOf(tag) > -1) {
23216 this.cwhite.push(tag);
23220 Roo.each(w, function(tag) {
23221 if (b.indexOf(tag) > -1) {
23224 if (this.cwhite.indexOf(tag) > -1) {
23227 this.cwhite.push(tag);
23232 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23233 if (w.indexOf(tag) > -1) {
23236 this.cblack.push(tag);
23240 Roo.each(b, function(tag) {
23241 if (w.indexOf(tag) > -1) {
23244 if (this.cblack.indexOf(tag) > -1) {
23247 this.cblack.push(tag);
23252 setStylesheets : function(stylesheets)
23254 if(typeof(stylesheets) == 'string'){
23255 Roo.get(this.iframe.contentDocument.head).createChild({
23257 rel : 'stylesheet',
23266 Roo.each(stylesheets, function(s) {
23271 Roo.get(_this.iframe.contentDocument.head).createChild({
23273 rel : 'stylesheet',
23282 removeStylesheets : function()
23286 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23291 setStyle : function(style)
23293 Roo.get(this.iframe.contentDocument.head).createChild({
23302 // hide stuff that is not compatible
23316 * @event specialkey
23320 * @cfg {String} fieldClass @hide
23323 * @cfg {String} focusClass @hide
23326 * @cfg {String} autoCreate @hide
23329 * @cfg {String} inputType @hide
23332 * @cfg {String} invalidClass @hide
23335 * @cfg {String} invalidText @hide
23338 * @cfg {String} msgFx @hide
23341 * @cfg {String} validateOnBlur @hide
23345 Roo.HtmlEditorCore.white = [
23346 'area', 'br', 'img', 'input', 'hr', 'wbr',
23348 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23349 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23350 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23351 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23352 'table', 'ul', 'xmp',
23354 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23357 'dir', 'menu', 'ol', 'ul', 'dl',
23363 Roo.HtmlEditorCore.black = [
23364 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23366 'base', 'basefont', 'bgsound', 'blink', 'body',
23367 'frame', 'frameset', 'head', 'html', 'ilayer',
23368 'iframe', 'layer', 'link', 'meta', 'object',
23369 'script', 'style' ,'title', 'xml' // clean later..
23371 Roo.HtmlEditorCore.clean = [
23372 'script', 'style', 'title', 'xml'
23374 Roo.HtmlEditorCore.remove = [
23379 Roo.HtmlEditorCore.ablack = [
23383 Roo.HtmlEditorCore.aclean = [
23384 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23388 Roo.HtmlEditorCore.pwhite= [
23389 'http', 'https', 'mailto'
23392 // white listed style attributes.
23393 Roo.HtmlEditorCore.cwhite= [
23394 // 'text-align', /// default is to allow most things..
23400 // black listed style attributes.
23401 Roo.HtmlEditorCore.cblack= [
23402 // 'font-size' -- this can be set by the project
23406 Roo.HtmlEditorCore.swapCodes =[
23425 * @class Roo.bootstrap.HtmlEditor
23426 * @extends Roo.bootstrap.TextArea
23427 * Bootstrap HtmlEditor class
23430 * Create a new HtmlEditor
23431 * @param {Object} config The config object
23434 Roo.bootstrap.HtmlEditor = function(config){
23435 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23436 if (!this.toolbars) {
23437 this.toolbars = [];
23440 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23443 * @event initialize
23444 * Fires when the editor is fully initialized (including the iframe)
23445 * @param {HtmlEditor} this
23450 * Fires when the editor is first receives the focus. Any insertion must wait
23451 * until after this event.
23452 * @param {HtmlEditor} this
23456 * @event beforesync
23457 * Fires before the textarea is updated with content from the editor iframe. Return false
23458 * to cancel the sync.
23459 * @param {HtmlEditor} this
23460 * @param {String} html
23464 * @event beforepush
23465 * Fires before the iframe editor is updated with content from the textarea. Return false
23466 * to cancel the push.
23467 * @param {HtmlEditor} this
23468 * @param {String} html
23473 * Fires when the textarea is updated with content from the editor iframe.
23474 * @param {HtmlEditor} this
23475 * @param {String} html
23480 * Fires when the iframe editor is updated with content from the textarea.
23481 * @param {HtmlEditor} this
23482 * @param {String} html
23486 * @event editmodechange
23487 * Fires when the editor switches edit modes
23488 * @param {HtmlEditor} this
23489 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23491 editmodechange: true,
23493 * @event editorevent
23494 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23495 * @param {HtmlEditor} this
23499 * @event firstfocus
23500 * Fires when on first focus - needed by toolbars..
23501 * @param {HtmlEditor} this
23506 * Auto save the htmlEditor value as a file into Events
23507 * @param {HtmlEditor} this
23511 * @event savedpreview
23512 * preview the saved version of htmlEditor
23513 * @param {HtmlEditor} this
23520 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23524 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23529 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23534 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23539 * @cfg {Number} height (in pixels)
23543 * @cfg {Number} width (in pixels)
23548 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23551 stylesheets: false,
23556 // private properties
23557 validationEvent : false,
23559 initialized : false,
23562 onFocus : Roo.emptyFn,
23564 hideMode:'offsets',
23566 tbContainer : false,
23570 toolbarContainer :function() {
23571 return this.wrap.select('.x-html-editor-tb',true).first();
23575 * Protected method that will not generally be called directly. It
23576 * is called when the editor creates its toolbar. Override this method if you need to
23577 * add custom toolbar buttons.
23578 * @param {HtmlEditor} editor
23580 createToolbar : function(){
23581 Roo.log('renewing');
23582 Roo.log("create toolbars");
23584 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23585 this.toolbars[0].render(this.toolbarContainer());
23589 // if (!editor.toolbars || !editor.toolbars.length) {
23590 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23593 // for (var i =0 ; i < editor.toolbars.length;i++) {
23594 // editor.toolbars[i] = Roo.factory(
23595 // typeof(editor.toolbars[i]) == 'string' ?
23596 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23597 // Roo.bootstrap.HtmlEditor);
23598 // editor.toolbars[i].init(editor);
23604 onRender : function(ct, position)
23606 // Roo.log("Call onRender: " + this.xtype);
23608 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23610 this.wrap = this.inputEl().wrap({
23611 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23614 this.editorcore.onRender(ct, position);
23616 if (this.resizable) {
23617 this.resizeEl = new Roo.Resizable(this.wrap, {
23621 minHeight : this.height,
23622 height: this.height,
23623 handles : this.resizable,
23626 resize : function(r, w, h) {
23627 _t.onResize(w,h); // -something
23633 this.createToolbar(this);
23636 if(!this.width && this.resizable){
23637 this.setSize(this.wrap.getSize());
23639 if (this.resizeEl) {
23640 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23641 // should trigger onReize..
23647 onResize : function(w, h)
23649 Roo.log('resize: ' +w + ',' + h );
23650 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23654 if(this.inputEl() ){
23655 if(typeof w == 'number'){
23656 var aw = w - this.wrap.getFrameWidth('lr');
23657 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23660 if(typeof h == 'number'){
23661 var tbh = -11; // fixme it needs to tool bar size!
23662 for (var i =0; i < this.toolbars.length;i++) {
23663 // fixme - ask toolbars for heights?
23664 tbh += this.toolbars[i].el.getHeight();
23665 //if (this.toolbars[i].footer) {
23666 // tbh += this.toolbars[i].footer.el.getHeight();
23674 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23675 ah -= 5; // knock a few pixes off for look..
23676 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23680 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23681 this.editorcore.onResize(ew,eh);
23686 * Toggles the editor between standard and source edit mode.
23687 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23689 toggleSourceEdit : function(sourceEditMode)
23691 this.editorcore.toggleSourceEdit(sourceEditMode);
23693 if(this.editorcore.sourceEditMode){
23694 Roo.log('editor - showing textarea');
23697 // Roo.log(this.syncValue());
23699 this.inputEl().removeClass(['hide', 'x-hidden']);
23700 this.inputEl().dom.removeAttribute('tabIndex');
23701 this.inputEl().focus();
23703 Roo.log('editor - hiding textarea');
23705 // Roo.log(this.pushValue());
23708 this.inputEl().addClass(['hide', 'x-hidden']);
23709 this.inputEl().dom.setAttribute('tabIndex', -1);
23710 //this.deferFocus();
23713 if(this.resizable){
23714 this.setSize(this.wrap.getSize());
23717 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23720 // private (for BoxComponent)
23721 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23723 // private (for BoxComponent)
23724 getResizeEl : function(){
23728 // private (for BoxComponent)
23729 getPositionEl : function(){
23734 initEvents : function(){
23735 this.originalValue = this.getValue();
23739 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23742 // markInvalid : Roo.emptyFn,
23744 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23747 // clearInvalid : Roo.emptyFn,
23749 setValue : function(v){
23750 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23751 this.editorcore.pushValue();
23756 deferFocus : function(){
23757 this.focus.defer(10, this);
23761 focus : function(){
23762 this.editorcore.focus();
23768 onDestroy : function(){
23774 for (var i =0; i < this.toolbars.length;i++) {
23775 // fixme - ask toolbars for heights?
23776 this.toolbars[i].onDestroy();
23779 this.wrap.dom.innerHTML = '';
23780 this.wrap.remove();
23785 onFirstFocus : function(){
23786 //Roo.log("onFirstFocus");
23787 this.editorcore.onFirstFocus();
23788 for (var i =0; i < this.toolbars.length;i++) {
23789 this.toolbars[i].onFirstFocus();
23795 syncValue : function()
23797 this.editorcore.syncValue();
23800 pushValue : function()
23802 this.editorcore.pushValue();
23806 // hide stuff that is not compatible
23820 * @event specialkey
23824 * @cfg {String} fieldClass @hide
23827 * @cfg {String} focusClass @hide
23830 * @cfg {String} autoCreate @hide
23833 * @cfg {String} inputType @hide
23836 * @cfg {String} invalidClass @hide
23839 * @cfg {String} invalidText @hide
23842 * @cfg {String} msgFx @hide
23845 * @cfg {String} validateOnBlur @hide
23854 Roo.namespace('Roo.bootstrap.htmleditor');
23856 * @class Roo.bootstrap.HtmlEditorToolbar1
23861 new Roo.bootstrap.HtmlEditor({
23864 new Roo.bootstrap.HtmlEditorToolbar1({
23865 disable : { fonts: 1 , format: 1, ..., ... , ...],
23871 * @cfg {Object} disable List of elements to disable..
23872 * @cfg {Array} btns List of additional buttons.
23876 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23879 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23882 Roo.apply(this, config);
23884 // default disabled, based on 'good practice'..
23885 this.disable = this.disable || {};
23886 Roo.applyIf(this.disable, {
23889 specialElements : true
23891 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23893 this.editor = config.editor;
23894 this.editorcore = config.editor.editorcore;
23896 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23898 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23899 // dont call parent... till later.
23901 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23906 editorcore : false,
23911 "h1","h2","h3","h4","h5","h6",
23913 "abbr", "acronym", "address", "cite", "samp", "var",
23917 onRender : function(ct, position)
23919 // Roo.log("Call onRender: " + this.xtype);
23921 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23923 this.el.dom.style.marginBottom = '0';
23925 var editorcore = this.editorcore;
23926 var editor= this.editor;
23929 var btn = function(id,cmd , toggle, handler, html){
23931 var event = toggle ? 'toggle' : 'click';
23936 xns: Roo.bootstrap,
23939 enableToggle:toggle !== false,
23941 pressed : toggle ? false : null,
23944 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23945 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23951 // var cb_box = function...
23956 xns: Roo.bootstrap,
23957 glyphicon : 'font',
23961 xns: Roo.bootstrap,
23965 Roo.each(this.formats, function(f) {
23966 style.menu.items.push({
23968 xns: Roo.bootstrap,
23969 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23974 editorcore.insertTag(this.tagname);
23981 children.push(style);
23983 btn('bold',false,true);
23984 btn('italic',false,true);
23985 btn('align-left', 'justifyleft',true);
23986 btn('align-center', 'justifycenter',true);
23987 btn('align-right' , 'justifyright',true);
23988 btn('link', false, false, function(btn) {
23989 //Roo.log("create link?");
23990 var url = prompt(this.createLinkText, this.defaultLinkValue);
23991 if(url && url != 'http:/'+'/'){
23992 this.editorcore.relayCmd('createlink', url);
23995 btn('list','insertunorderedlist',true);
23996 btn('pencil', false,true, function(btn){
23998 this.toggleSourceEdit(btn.pressed);
24001 if (this.editor.btns.length > 0) {
24002 for (var i = 0; i<this.editor.btns.length; i++) {
24003 children.push(this.editor.btns[i]);
24011 xns: Roo.bootstrap,
24016 xns: Roo.bootstrap,
24021 cog.menu.items.push({
24023 xns: Roo.bootstrap,
24024 html : Clean styles,
24029 editorcore.insertTag(this.tagname);
24038 this.xtype = 'NavSimplebar';
24040 for(var i=0;i< children.length;i++) {
24042 this.buttons.add(this.addxtypeChild(children[i]));
24046 editor.on('editorevent', this.updateToolbar, this);
24048 onBtnClick : function(id)
24050 this.editorcore.relayCmd(id);
24051 this.editorcore.focus();
24055 * Protected method that will not generally be called directly. It triggers
24056 * a toolbar update by reading the markup state of the current selection in the editor.
24058 updateToolbar: function(){
24060 if(!this.editorcore.activated){
24061 this.editor.onFirstFocus(); // is this neeed?
24065 var btns = this.buttons;
24066 var doc = this.editorcore.doc;
24067 btns.get('bold').setActive(doc.queryCommandState('bold'));
24068 btns.get('italic').setActive(doc.queryCommandState('italic'));
24069 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24071 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24072 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24073 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24075 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24076 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24079 var ans = this.editorcore.getAllAncestors();
24080 if (this.formatCombo) {
24083 var store = this.formatCombo.store;
24084 this.formatCombo.setValue("");
24085 for (var i =0; i < ans.length;i++) {
24086 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24088 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24096 // hides menus... - so this cant be on a menu...
24097 Roo.bootstrap.MenuMgr.hideAll();
24099 Roo.bootstrap.MenuMgr.hideAll();
24100 //this.editorsyncValue();
24102 onFirstFocus: function() {
24103 this.buttons.each(function(item){
24107 toggleSourceEdit : function(sourceEditMode){
24110 if(sourceEditMode){
24111 Roo.log("disabling buttons");
24112 this.buttons.each( function(item){
24113 if(item.cmd != 'pencil'){
24119 Roo.log("enabling buttons");
24120 if(this.editorcore.initialized){
24121 this.buttons.each( function(item){
24127 Roo.log("calling toggole on editor");
24128 // tell the editor that it's been pressed..
24129 this.editor.toggleSourceEdit(sourceEditMode);
24139 * @class Roo.bootstrap.Table.AbstractSelectionModel
24140 * @extends Roo.util.Observable
24141 * Abstract base class for grid SelectionModels. It provides the interface that should be
24142 * implemented by descendant classes. This class should not be directly instantiated.
24145 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24146 this.locked = false;
24147 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24151 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24152 /** @ignore Called by the grid automatically. Do not call directly. */
24153 init : function(grid){
24159 * Locks the selections.
24162 this.locked = true;
24166 * Unlocks the selections.
24168 unlock : function(){
24169 this.locked = false;
24173 * Returns true if the selections are locked.
24174 * @return {Boolean}
24176 isLocked : function(){
24177 return this.locked;
24181 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24182 * @class Roo.bootstrap.Table.RowSelectionModel
24183 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24184 * It supports multiple selections and keyboard selection/navigation.
24186 * @param {Object} config
24189 Roo.bootstrap.Table.RowSelectionModel = function(config){
24190 Roo.apply(this, config);
24191 this.selections = new Roo.util.MixedCollection(false, function(o){
24196 this.lastActive = false;
24200 * @event selectionchange
24201 * Fires when the selection changes
24202 * @param {SelectionModel} this
24204 "selectionchange" : true,
24206 * @event afterselectionchange
24207 * Fires after the selection changes (eg. by key press or clicking)
24208 * @param {SelectionModel} this
24210 "afterselectionchange" : true,
24212 * @event beforerowselect
24213 * Fires when a row is selected being selected, return false to cancel.
24214 * @param {SelectionModel} this
24215 * @param {Number} rowIndex The selected index
24216 * @param {Boolean} keepExisting False if other selections will be cleared
24218 "beforerowselect" : true,
24221 * Fires when a row is selected.
24222 * @param {SelectionModel} this
24223 * @param {Number} rowIndex The selected index
24224 * @param {Roo.data.Record} r The record
24226 "rowselect" : true,
24228 * @event rowdeselect
24229 * Fires when a row is deselected.
24230 * @param {SelectionModel} this
24231 * @param {Number} rowIndex The selected index
24233 "rowdeselect" : true
24235 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24236 this.locked = false;
24239 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24241 * @cfg {Boolean} singleSelect
24242 * True to allow selection of only one row at a time (defaults to false)
24244 singleSelect : false,
24247 initEvents : function()
24250 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24251 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24252 //}else{ // allow click to work like normal
24253 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24255 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24256 this.grid.on("rowclick", this.handleMouseDown, this);
24258 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24259 "up" : function(e){
24261 this.selectPrevious(e.shiftKey);
24262 }else if(this.last !== false && this.lastActive !== false){
24263 var last = this.last;
24264 this.selectRange(this.last, this.lastActive-1);
24265 this.grid.getView().focusRow(this.lastActive);
24266 if(last !== false){
24270 this.selectFirstRow();
24272 this.fireEvent("afterselectionchange", this);
24274 "down" : function(e){
24276 this.selectNext(e.shiftKey);
24277 }else if(this.last !== false && this.lastActive !== false){
24278 var last = this.last;
24279 this.selectRange(this.last, this.lastActive+1);
24280 this.grid.getView().focusRow(this.lastActive);
24281 if(last !== false){
24285 this.selectFirstRow();
24287 this.fireEvent("afterselectionchange", this);
24291 this.grid.store.on('load', function(){
24292 this.selections.clear();
24295 var view = this.grid.view;
24296 view.on("refresh", this.onRefresh, this);
24297 view.on("rowupdated", this.onRowUpdated, this);
24298 view.on("rowremoved", this.onRemove, this);
24303 onRefresh : function()
24305 var ds = this.grid.store, i, v = this.grid.view;
24306 var s = this.selections;
24307 s.each(function(r){
24308 if((i = ds.indexOfId(r.id)) != -1){
24317 onRemove : function(v, index, r){
24318 this.selections.remove(r);
24322 onRowUpdated : function(v, index, r){
24323 if(this.isSelected(r)){
24324 v.onRowSelect(index);
24330 * @param {Array} records The records to select
24331 * @param {Boolean} keepExisting (optional) True to keep existing selections
24333 selectRecords : function(records, keepExisting)
24336 this.clearSelections();
24338 var ds = this.grid.store;
24339 for(var i = 0, len = records.length; i < len; i++){
24340 this.selectRow(ds.indexOf(records[i]), true);
24345 * Gets the number of selected rows.
24348 getCount : function(){
24349 return this.selections.length;
24353 * Selects the first row in the grid.
24355 selectFirstRow : function(){
24360 * Select the last row.
24361 * @param {Boolean} keepExisting (optional) True to keep existing selections
24363 selectLastRow : function(keepExisting){
24364 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24365 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24369 * Selects the row immediately following the last selected row.
24370 * @param {Boolean} keepExisting (optional) True to keep existing selections
24372 selectNext : function(keepExisting)
24374 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24375 this.selectRow(this.last+1, keepExisting);
24376 this.grid.getView().focusRow(this.last);
24381 * Selects the row that precedes the last selected row.
24382 * @param {Boolean} keepExisting (optional) True to keep existing selections
24384 selectPrevious : function(keepExisting){
24386 this.selectRow(this.last-1, keepExisting);
24387 this.grid.getView().focusRow(this.last);
24392 * Returns the selected records
24393 * @return {Array} Array of selected records
24395 getSelections : function(){
24396 return [].concat(this.selections.items);
24400 * Returns the first selected record.
24403 getSelected : function(){
24404 return this.selections.itemAt(0);
24409 * Clears all selections.
24411 clearSelections : function(fast)
24417 var ds = this.grid.store;
24418 var s = this.selections;
24419 s.each(function(r){
24420 this.deselectRow(ds.indexOfId(r.id));
24424 this.selections.clear();
24431 * Selects all rows.
24433 selectAll : function(){
24437 this.selections.clear();
24438 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24439 this.selectRow(i, true);
24444 * Returns True if there is a selection.
24445 * @return {Boolean}
24447 hasSelection : function(){
24448 return this.selections.length > 0;
24452 * Returns True if the specified row is selected.
24453 * @param {Number/Record} record The record or index of the record to check
24454 * @return {Boolean}
24456 isSelected : function(index){
24457 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24458 return (r && this.selections.key(r.id) ? true : false);
24462 * Returns True if the specified record id is selected.
24463 * @param {String} id The id of record to check
24464 * @return {Boolean}
24466 isIdSelected : function(id){
24467 return (this.selections.key(id) ? true : false);
24472 handleMouseDBClick : function(e, t){
24476 handleMouseDown : function(e, t)
24478 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24479 if(this.isLocked() || rowIndex < 0 ){
24482 if(e.shiftKey && this.last !== false){
24483 var last = this.last;
24484 this.selectRange(last, rowIndex, e.ctrlKey);
24485 this.last = last; // reset the last
24489 var isSelected = this.isSelected(rowIndex);
24490 //Roo.log("select row:" + rowIndex);
24492 this.deselectRow(rowIndex);
24494 this.selectRow(rowIndex, true);
24498 if(e.button !== 0 && isSelected){
24499 alert('rowIndex 2: ' + rowIndex);
24500 view.focusRow(rowIndex);
24501 }else if(e.ctrlKey && isSelected){
24502 this.deselectRow(rowIndex);
24503 }else if(!isSelected){
24504 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24505 view.focusRow(rowIndex);
24509 this.fireEvent("afterselectionchange", this);
24512 handleDragableRowClick : function(grid, rowIndex, e)
24514 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24515 this.selectRow(rowIndex, false);
24516 grid.view.focusRow(rowIndex);
24517 this.fireEvent("afterselectionchange", this);
24522 * Selects multiple rows.
24523 * @param {Array} rows Array of the indexes of the row to select
24524 * @param {Boolean} keepExisting (optional) True to keep existing selections
24526 selectRows : function(rows, keepExisting){
24528 this.clearSelections();
24530 for(var i = 0, len = rows.length; i < len; i++){
24531 this.selectRow(rows[i], true);
24536 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24537 * @param {Number} startRow The index of the first row in the range
24538 * @param {Number} endRow The index of the last row in the range
24539 * @param {Boolean} keepExisting (optional) True to retain existing selections
24541 selectRange : function(startRow, endRow, keepExisting){
24546 this.clearSelections();
24548 if(startRow <= endRow){
24549 for(var i = startRow; i <= endRow; i++){
24550 this.selectRow(i, true);
24553 for(var i = startRow; i >= endRow; i--){
24554 this.selectRow(i, true);
24560 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24561 * @param {Number} startRow The index of the first row in the range
24562 * @param {Number} endRow The index of the last row in the range
24564 deselectRange : function(startRow, endRow, preventViewNotify){
24568 for(var i = startRow; i <= endRow; i++){
24569 this.deselectRow(i, preventViewNotify);
24575 * @param {Number} row The index of the row to select
24576 * @param {Boolean} keepExisting (optional) True to keep existing selections
24578 selectRow : function(index, keepExisting, preventViewNotify)
24580 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24583 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24584 if(!keepExisting || this.singleSelect){
24585 this.clearSelections();
24588 var r = this.grid.store.getAt(index);
24589 //console.log('selectRow - record id :' + r.id);
24591 this.selections.add(r);
24592 this.last = this.lastActive = index;
24593 if(!preventViewNotify){
24594 var proxy = new Roo.Element(
24595 this.grid.getRowDom(index)
24597 proxy.addClass('bg-info info');
24599 this.fireEvent("rowselect", this, index, r);
24600 this.fireEvent("selectionchange", this);
24606 * @param {Number} row The index of the row to deselect
24608 deselectRow : function(index, preventViewNotify)
24613 if(this.last == index){
24616 if(this.lastActive == index){
24617 this.lastActive = false;
24620 var r = this.grid.store.getAt(index);
24625 this.selections.remove(r);
24626 //.console.log('deselectRow - record id :' + r.id);
24627 if(!preventViewNotify){
24629 var proxy = new Roo.Element(
24630 this.grid.getRowDom(index)
24632 proxy.removeClass('bg-info info');
24634 this.fireEvent("rowdeselect", this, index);
24635 this.fireEvent("selectionchange", this);
24639 restoreLast : function(){
24641 this.last = this._last;
24646 acceptsNav : function(row, col, cm){
24647 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24651 onEditorKey : function(field, e){
24652 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24657 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24659 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24661 }else if(k == e.ENTER && !e.ctrlKey){
24665 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24667 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24669 }else if(k == e.ESC){
24673 g.startEditing(newCell[0], newCell[1]);
24679 * Ext JS Library 1.1.1
24680 * Copyright(c) 2006-2007, Ext JS, LLC.
24682 * Originally Released Under LGPL - original licence link has changed is not relivant.
24685 * <script type="text/javascript">
24689 * @class Roo.bootstrap.PagingToolbar
24690 * @extends Roo.bootstrap.NavSimplebar
24691 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24693 * Create a new PagingToolbar
24694 * @param {Object} config The config object
24695 * @param {Roo.data.Store} store
24697 Roo.bootstrap.PagingToolbar = function(config)
24699 // old args format still supported... - xtype is prefered..
24700 // created from xtype...
24702 this.ds = config.dataSource;
24704 if (config.store && !this.ds) {
24705 this.store= Roo.factory(config.store, Roo.data);
24706 this.ds = this.store;
24707 this.ds.xmodule = this.xmodule || false;
24710 this.toolbarItems = [];
24711 if (config.items) {
24712 this.toolbarItems = config.items;
24715 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24720 this.bind(this.ds);
24723 if (Roo.bootstrap.version == 4) {
24724 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24726 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24731 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24733 * @cfg {Roo.data.Store} dataSource
24734 * The underlying data store providing the paged data
24737 * @cfg {String/HTMLElement/Element} container
24738 * container The id or element that will contain the toolbar
24741 * @cfg {Boolean} displayInfo
24742 * True to display the displayMsg (defaults to false)
24745 * @cfg {Number} pageSize
24746 * The number of records to display per page (defaults to 20)
24750 * @cfg {String} displayMsg
24751 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24753 displayMsg : 'Displaying {0} - {1} of {2}',
24755 * @cfg {String} emptyMsg
24756 * The message to display when no records are found (defaults to "No data to display")
24758 emptyMsg : 'No data to display',
24760 * Customizable piece of the default paging text (defaults to "Page")
24763 beforePageText : "Page",
24765 * Customizable piece of the default paging text (defaults to "of %0")
24768 afterPageText : "of {0}",
24770 * Customizable piece of the default paging text (defaults to "First Page")
24773 firstText : "First Page",
24775 * Customizable piece of the default paging text (defaults to "Previous Page")
24778 prevText : "Previous Page",
24780 * Customizable piece of the default paging text (defaults to "Next Page")
24783 nextText : "Next Page",
24785 * Customizable piece of the default paging text (defaults to "Last Page")
24788 lastText : "Last Page",
24790 * Customizable piece of the default paging text (defaults to "Refresh")
24793 refreshText : "Refresh",
24797 onRender : function(ct, position)
24799 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24800 this.navgroup.parentId = this.id;
24801 this.navgroup.onRender(this.el, null);
24802 // add the buttons to the navgroup
24804 if(this.displayInfo){
24805 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24806 this.displayEl = this.el.select('.x-paging-info', true).first();
24807 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24808 // this.displayEl = navel.el.select('span',true).first();
24814 Roo.each(_this.buttons, function(e){ // this might need to use render????
24815 Roo.factory(e).render(_this.el);
24819 Roo.each(_this.toolbarItems, function(e) {
24820 _this.navgroup.addItem(e);
24824 this.first = this.navgroup.addItem({
24825 tooltip: this.firstText,
24826 cls: "prev btn-outline-secondary",
24827 html : ' <i class="fa fa-step-backward"></i>',
24829 preventDefault: true,
24830 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24833 this.prev = this.navgroup.addItem({
24834 tooltip: this.prevText,
24835 cls: "prev btn-outline-secondary",
24836 html : ' <i class="fa fa-backward"></i>',
24838 preventDefault: true,
24839 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24841 //this.addSeparator();
24844 var field = this.navgroup.addItem( {
24846 cls : 'x-paging-position btn-outline-secondary',
24848 html : this.beforePageText +
24849 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24850 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24853 this.field = field.el.select('input', true).first();
24854 this.field.on("keydown", this.onPagingKeydown, this);
24855 this.field.on("focus", function(){this.dom.select();});
24858 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24859 //this.field.setHeight(18);
24860 //this.addSeparator();
24861 this.next = this.navgroup.addItem({
24862 tooltip: this.nextText,
24863 cls: "next btn-outline-secondary",
24864 html : ' <i class="fa fa-forward"></i>',
24866 preventDefault: true,
24867 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24869 this.last = this.navgroup.addItem({
24870 tooltip: this.lastText,
24871 html : ' <i class="fa fa-step-forward"></i>',
24872 cls: "next btn-outline-secondary",
24874 preventDefault: true,
24875 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24877 //this.addSeparator();
24878 this.loading = this.navgroup.addItem({
24879 tooltip: this.refreshText,
24880 cls: "btn-outline-secondary",
24881 html : ' <i class="fa fa-refresh"></i>',
24882 preventDefault: true,
24883 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24889 updateInfo : function(){
24890 if(this.displayEl){
24891 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24892 var msg = count == 0 ?
24896 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24898 this.displayEl.update(msg);
24903 onLoad : function(ds, r, o)
24905 this.cursor = o.params.start ? o.params.start : 0;
24907 var d = this.getPageData(),
24912 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24913 this.field.dom.value = ap;
24914 this.first.setDisabled(ap == 1);
24915 this.prev.setDisabled(ap == 1);
24916 this.next.setDisabled(ap == ps);
24917 this.last.setDisabled(ap == ps);
24918 this.loading.enable();
24923 getPageData : function(){
24924 var total = this.ds.getTotalCount();
24927 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24928 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24933 onLoadError : function(){
24934 this.loading.enable();
24938 onPagingKeydown : function(e){
24939 var k = e.getKey();
24940 var d = this.getPageData();
24942 var v = this.field.dom.value, pageNum;
24943 if(!v || isNaN(pageNum = parseInt(v, 10))){
24944 this.field.dom.value = d.activePage;
24947 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24948 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24951 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))
24953 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24954 this.field.dom.value = pageNum;
24955 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24958 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24960 var v = this.field.dom.value, pageNum;
24961 var increment = (e.shiftKey) ? 10 : 1;
24962 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24965 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24966 this.field.dom.value = d.activePage;
24969 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24971 this.field.dom.value = parseInt(v, 10) + increment;
24972 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24973 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24980 beforeLoad : function(){
24982 this.loading.disable();
24987 onClick : function(which){
24996 ds.load({params:{start: 0, limit: this.pageSize}});
24999 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25002 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25005 var total = ds.getTotalCount();
25006 var extra = total % this.pageSize;
25007 var lastStart = extra ? (total - extra) : total-this.pageSize;
25008 ds.load({params:{start: lastStart, limit: this.pageSize}});
25011 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25017 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25018 * @param {Roo.data.Store} store The data store to unbind
25020 unbind : function(ds){
25021 ds.un("beforeload", this.beforeLoad, this);
25022 ds.un("load", this.onLoad, this);
25023 ds.un("loadexception", this.onLoadError, this);
25024 ds.un("remove", this.updateInfo, this);
25025 ds.un("add", this.updateInfo, this);
25026 this.ds = undefined;
25030 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25031 * @param {Roo.data.Store} store The data store to bind
25033 bind : function(ds){
25034 ds.on("beforeload", this.beforeLoad, this);
25035 ds.on("load", this.onLoad, this);
25036 ds.on("loadexception", this.onLoadError, this);
25037 ds.on("remove", this.updateInfo, this);
25038 ds.on("add", this.updateInfo, this);
25049 * @class Roo.bootstrap.MessageBar
25050 * @extends Roo.bootstrap.Component
25051 * Bootstrap MessageBar class
25052 * @cfg {String} html contents of the MessageBar
25053 * @cfg {String} weight (info | success | warning | danger) default info
25054 * @cfg {String} beforeClass insert the bar before the given class
25055 * @cfg {Boolean} closable (true | false) default false
25056 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25059 * Create a new Element
25060 * @param {Object} config The config object
25063 Roo.bootstrap.MessageBar = function(config){
25064 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25067 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25073 beforeClass: 'bootstrap-sticky-wrap',
25075 getAutoCreate : function(){
25079 cls: 'alert alert-dismissable alert-' + this.weight,
25084 html: this.html || ''
25090 cfg.cls += ' alert-messages-fixed';
25104 onRender : function(ct, position)
25106 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25109 var cfg = Roo.apply({}, this.getAutoCreate());
25113 cfg.cls += ' ' + this.cls;
25116 cfg.style = this.style;
25118 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25120 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25123 this.el.select('>button.close').on('click', this.hide, this);
25129 if (!this.rendered) {
25135 this.fireEvent('show', this);
25141 if (!this.rendered) {
25147 this.fireEvent('hide', this);
25150 update : function()
25152 // var e = this.el.dom.firstChild;
25154 // if(this.closable){
25155 // e = e.nextSibling;
25158 // e.data = this.html || '';
25160 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25176 * @class Roo.bootstrap.Graph
25177 * @extends Roo.bootstrap.Component
25178 * Bootstrap Graph class
25182 @cfg {String} graphtype bar | vbar | pie
25183 @cfg {number} g_x coodinator | centre x (pie)
25184 @cfg {number} g_y coodinator | centre y (pie)
25185 @cfg {number} g_r radius (pie)
25186 @cfg {number} g_height height of the chart (respected by all elements in the set)
25187 @cfg {number} g_width width of the chart (respected by all elements in the set)
25188 @cfg {Object} title The title of the chart
25191 -opts (object) options for the chart
25193 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25194 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25196 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.
25197 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25199 o stretch (boolean)
25201 -opts (object) options for the pie
25204 o startAngle (number)
25205 o endAngle (number)
25209 * Create a new Input
25210 * @param {Object} config The config object
25213 Roo.bootstrap.Graph = function(config){
25214 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25220 * The img click event for the img.
25221 * @param {Roo.EventObject} e
25227 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25238 //g_colors: this.colors,
25245 getAutoCreate : function(){
25256 onRender : function(ct,position){
25259 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25261 if (typeof(Raphael) == 'undefined') {
25262 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25266 this.raphael = Raphael(this.el.dom);
25268 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25269 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25270 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25271 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25273 r.text(160, 10, "Single Series Chart").attr(txtattr);
25274 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25275 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25276 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25278 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25279 r.barchart(330, 10, 300, 220, data1);
25280 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25281 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25284 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25285 // r.barchart(30, 30, 560, 250, xdata, {
25286 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25287 // axis : "0 0 1 1",
25288 // axisxlabels : xdata
25289 // //yvalues : cols,
25292 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25294 // this.load(null,xdata,{
25295 // axis : "0 0 1 1",
25296 // axisxlabels : xdata
25301 load : function(graphtype,xdata,opts)
25303 this.raphael.clear();
25305 graphtype = this.graphtype;
25310 var r = this.raphael,
25311 fin = function () {
25312 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25314 fout = function () {
25315 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25317 pfin = function() {
25318 this.sector.stop();
25319 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25322 this.label[0].stop();
25323 this.label[0].attr({ r: 7.5 });
25324 this.label[1].attr({ "font-weight": 800 });
25327 pfout = function() {
25328 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25331 this.label[0].animate({ r: 5 }, 500, "bounce");
25332 this.label[1].attr({ "font-weight": 400 });
25338 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25341 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25344 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25345 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25347 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25354 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25359 setTitle: function(o)
25364 initEvents: function() {
25367 this.el.on('click', this.onClick, this);
25371 onClick : function(e)
25373 Roo.log('img onclick');
25374 this.fireEvent('click', this, e);
25386 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25389 * @class Roo.bootstrap.dash.NumberBox
25390 * @extends Roo.bootstrap.Component
25391 * Bootstrap NumberBox class
25392 * @cfg {String} headline Box headline
25393 * @cfg {String} content Box content
25394 * @cfg {String} icon Box icon
25395 * @cfg {String} footer Footer text
25396 * @cfg {String} fhref Footer href
25399 * Create a new NumberBox
25400 * @param {Object} config The config object
25404 Roo.bootstrap.dash.NumberBox = function(config){
25405 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25409 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25418 getAutoCreate : function(){
25422 cls : 'small-box ',
25430 cls : 'roo-headline',
25431 html : this.headline
25435 cls : 'roo-content',
25436 html : this.content
25450 cls : 'ion ' + this.icon
25459 cls : 'small-box-footer',
25460 href : this.fhref || '#',
25464 cfg.cn.push(footer);
25471 onRender : function(ct,position){
25472 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25479 setHeadline: function (value)
25481 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25484 setFooter: function (value, href)
25486 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25489 this.el.select('a.small-box-footer',true).first().attr('href', href);
25494 setContent: function (value)
25496 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25499 initEvents: function()
25513 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25516 * @class Roo.bootstrap.dash.TabBox
25517 * @extends Roo.bootstrap.Component
25518 * Bootstrap TabBox class
25519 * @cfg {String} title Title of the TabBox
25520 * @cfg {String} icon Icon of the TabBox
25521 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25522 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25525 * Create a new TabBox
25526 * @param {Object} config The config object
25530 Roo.bootstrap.dash.TabBox = function(config){
25531 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25536 * When a pane is added
25537 * @param {Roo.bootstrap.dash.TabPane} pane
25541 * @event activatepane
25542 * When a pane is activated
25543 * @param {Roo.bootstrap.dash.TabPane} pane
25545 "activatepane" : true
25553 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25558 tabScrollable : false,
25560 getChildContainer : function()
25562 return this.el.select('.tab-content', true).first();
25565 getAutoCreate : function(){
25569 cls: 'pull-left header',
25577 cls: 'fa ' + this.icon
25583 cls: 'nav nav-tabs pull-right',
25589 if(this.tabScrollable){
25596 cls: 'nav nav-tabs pull-right',
25607 cls: 'nav-tabs-custom',
25612 cls: 'tab-content no-padding',
25620 initEvents : function()
25622 //Roo.log('add add pane handler');
25623 this.on('addpane', this.onAddPane, this);
25626 * Updates the box title
25627 * @param {String} html to set the title to.
25629 setTitle : function(value)
25631 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25633 onAddPane : function(pane)
25635 this.panes.push(pane);
25636 //Roo.log('addpane');
25638 // tabs are rendere left to right..
25639 if(!this.showtabs){
25643 var ctr = this.el.select('.nav-tabs', true).first();
25646 var existing = ctr.select('.nav-tab',true);
25647 var qty = existing.getCount();;
25650 var tab = ctr.createChild({
25652 cls : 'nav-tab' + (qty ? '' : ' active'),
25660 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25663 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25665 pane.el.addClass('active');
25670 onTabClick : function(ev,un,ob,pane)
25672 //Roo.log('tab - prev default');
25673 ev.preventDefault();
25676 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25677 pane.tab.addClass('active');
25678 //Roo.log(pane.title);
25679 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25680 // technically we should have a deactivate event.. but maybe add later.
25681 // and it should not de-activate the selected tab...
25682 this.fireEvent('activatepane', pane);
25683 pane.el.addClass('active');
25684 pane.fireEvent('activate');
25689 getActivePane : function()
25692 Roo.each(this.panes, function(p) {
25693 if(p.el.hasClass('active')){
25714 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25716 * @class Roo.bootstrap.TabPane
25717 * @extends Roo.bootstrap.Component
25718 * Bootstrap TabPane class
25719 * @cfg {Boolean} active (false | true) Default false
25720 * @cfg {String} title title of panel
25724 * Create a new TabPane
25725 * @param {Object} config The config object
25728 Roo.bootstrap.dash.TabPane = function(config){
25729 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25735 * When a pane is activated
25736 * @param {Roo.bootstrap.dash.TabPane} pane
25743 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25748 // the tabBox that this is attached to.
25751 getAutoCreate : function()
25759 cfg.cls += ' active';
25764 initEvents : function()
25766 //Roo.log('trigger add pane handler');
25767 this.parent().fireEvent('addpane', this)
25771 * Updates the tab title
25772 * @param {String} html to set the title to.
25774 setTitle: function(str)
25780 this.tab.select('a', true).first().dom.innerHTML = str;
25797 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25800 * @class Roo.bootstrap.menu.Menu
25801 * @extends Roo.bootstrap.Component
25802 * Bootstrap Menu class - container for Menu
25803 * @cfg {String} html Text of the menu
25804 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25805 * @cfg {String} icon Font awesome icon
25806 * @cfg {String} pos Menu align to (top | bottom) default bottom
25810 * Create a new Menu
25811 * @param {Object} config The config object
25815 Roo.bootstrap.menu.Menu = function(config){
25816 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25820 * @event beforeshow
25821 * Fires before this menu is displayed
25822 * @param {Roo.bootstrap.menu.Menu} this
25826 * @event beforehide
25827 * Fires before this menu is hidden
25828 * @param {Roo.bootstrap.menu.Menu} this
25833 * Fires after this menu is displayed
25834 * @param {Roo.bootstrap.menu.Menu} this
25839 * Fires after this menu is hidden
25840 * @param {Roo.bootstrap.menu.Menu} this
25845 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25846 * @param {Roo.bootstrap.menu.Menu} this
25847 * @param {Roo.EventObject} e
25854 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25858 weight : 'default',
25863 getChildContainer : function() {
25864 if(this.isSubMenu){
25868 return this.el.select('ul.dropdown-menu', true).first();
25871 getAutoCreate : function()
25876 cls : 'roo-menu-text',
25884 cls : 'fa ' + this.icon
25895 cls : 'dropdown-button btn btn-' + this.weight,
25900 cls : 'dropdown-toggle btn btn-' + this.weight,
25910 cls : 'dropdown-menu'
25916 if(this.pos == 'top'){
25917 cfg.cls += ' dropup';
25920 if(this.isSubMenu){
25923 cls : 'dropdown-menu'
25930 onRender : function(ct, position)
25932 this.isSubMenu = ct.hasClass('dropdown-submenu');
25934 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25937 initEvents : function()
25939 if(this.isSubMenu){
25943 this.hidden = true;
25945 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25946 this.triggerEl.on('click', this.onTriggerPress, this);
25948 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25949 this.buttonEl.on('click', this.onClick, this);
25955 if(this.isSubMenu){
25959 return this.el.select('ul.dropdown-menu', true).first();
25962 onClick : function(e)
25964 this.fireEvent("click", this, e);
25967 onTriggerPress : function(e)
25969 if (this.isVisible()) {
25976 isVisible : function(){
25977 return !this.hidden;
25982 this.fireEvent("beforeshow", this);
25984 this.hidden = false;
25985 this.el.addClass('open');
25987 Roo.get(document).on("mouseup", this.onMouseUp, this);
25989 this.fireEvent("show", this);
25996 this.fireEvent("beforehide", this);
25998 this.hidden = true;
25999 this.el.removeClass('open');
26001 Roo.get(document).un("mouseup", this.onMouseUp);
26003 this.fireEvent("hide", this);
26006 onMouseUp : function()
26020 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26023 * @class Roo.bootstrap.menu.Item
26024 * @extends Roo.bootstrap.Component
26025 * Bootstrap MenuItem class
26026 * @cfg {Boolean} submenu (true | false) default false
26027 * @cfg {String} html text of the item
26028 * @cfg {String} href the link
26029 * @cfg {Boolean} disable (true | false) default false
26030 * @cfg {Boolean} preventDefault (true | false) default true
26031 * @cfg {String} icon Font awesome icon
26032 * @cfg {String} pos Submenu align to (left | right) default right
26036 * Create a new Item
26037 * @param {Object} config The config object
26041 Roo.bootstrap.menu.Item = function(config){
26042 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26046 * Fires when the mouse is hovering over this menu
26047 * @param {Roo.bootstrap.menu.Item} this
26048 * @param {Roo.EventObject} e
26053 * Fires when the mouse exits this menu
26054 * @param {Roo.bootstrap.menu.Item} this
26055 * @param {Roo.EventObject} e
26061 * The raw click event for the entire grid.
26062 * @param {Roo.EventObject} e
26068 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26073 preventDefault: true,
26078 getAutoCreate : function()
26083 cls : 'roo-menu-item-text',
26091 cls : 'fa ' + this.icon
26100 href : this.href || '#',
26107 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26111 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26113 if(this.pos == 'left'){
26114 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26121 initEvents : function()
26123 this.el.on('mouseover', this.onMouseOver, this);
26124 this.el.on('mouseout', this.onMouseOut, this);
26126 this.el.select('a', true).first().on('click', this.onClick, this);
26130 onClick : function(e)
26132 if(this.preventDefault){
26133 e.preventDefault();
26136 this.fireEvent("click", this, e);
26139 onMouseOver : function(e)
26141 if(this.submenu && this.pos == 'left'){
26142 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26145 this.fireEvent("mouseover", this, e);
26148 onMouseOut : function(e)
26150 this.fireEvent("mouseout", this, e);
26162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26165 * @class Roo.bootstrap.menu.Separator
26166 * @extends Roo.bootstrap.Component
26167 * Bootstrap Separator class
26170 * Create a new Separator
26171 * @param {Object} config The config object
26175 Roo.bootstrap.menu.Separator = function(config){
26176 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26179 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26181 getAutoCreate : function(){
26202 * @class Roo.bootstrap.Tooltip
26203 * Bootstrap Tooltip class
26204 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26205 * to determine which dom element triggers the tooltip.
26207 * It needs to add support for additional attributes like tooltip-position
26210 * Create a new Toolti
26211 * @param {Object} config The config object
26214 Roo.bootstrap.Tooltip = function(config){
26215 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26217 this.alignment = Roo.bootstrap.Tooltip.alignment;
26219 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26220 this.alignment = config.alignment;
26225 Roo.apply(Roo.bootstrap.Tooltip, {
26227 * @function init initialize tooltip monitoring.
26231 currentTip : false,
26232 currentRegion : false,
26238 Roo.get(document).on('mouseover', this.enter ,this);
26239 Roo.get(document).on('mouseout', this.leave, this);
26242 this.currentTip = new Roo.bootstrap.Tooltip();
26245 enter : function(ev)
26247 var dom = ev.getTarget();
26249 //Roo.log(['enter',dom]);
26250 var el = Roo.fly(dom);
26251 if (this.currentEl) {
26253 //Roo.log(this.currentEl);
26254 //Roo.log(this.currentEl.contains(dom));
26255 if (this.currentEl == el) {
26258 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26264 if (this.currentTip.el) {
26265 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26269 if(!el || el.dom == document){
26275 // you can not look for children, as if el is the body.. then everythign is the child..
26276 if (!el.attr('tooltip')) { //
26277 if (!el.select("[tooltip]").elements.length) {
26280 // is the mouse over this child...?
26281 bindEl = el.select("[tooltip]").first();
26282 var xy = ev.getXY();
26283 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26284 //Roo.log("not in region.");
26287 //Roo.log("child element over..");
26290 this.currentEl = bindEl;
26291 this.currentTip.bind(bindEl);
26292 this.currentRegion = Roo.lib.Region.getRegion(dom);
26293 this.currentTip.enter();
26296 leave : function(ev)
26298 var dom = ev.getTarget();
26299 //Roo.log(['leave',dom]);
26300 if (!this.currentEl) {
26305 if (dom != this.currentEl.dom) {
26308 var xy = ev.getXY();
26309 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26312 // only activate leave if mouse cursor is outside... bounding box..
26317 if (this.currentTip) {
26318 this.currentTip.leave();
26320 //Roo.log('clear currentEl');
26321 this.currentEl = false;
26326 'left' : ['r-l', [-2,0], 'right'],
26327 'right' : ['l-r', [2,0], 'left'],
26328 'bottom' : ['t-b', [0,2], 'top'],
26329 'top' : [ 'b-t', [0,-2], 'bottom']
26335 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26340 delay : null, // can be { show : 300 , hide: 500}
26344 hoverState : null, //???
26346 placement : 'bottom',
26350 getAutoCreate : function(){
26357 cls : 'tooltip-arrow'
26360 cls : 'tooltip-inner'
26367 bind : function(el)
26373 enter : function () {
26375 if (this.timeout != null) {
26376 clearTimeout(this.timeout);
26379 this.hoverState = 'in';
26380 //Roo.log("enter - show");
26381 if (!this.delay || !this.delay.show) {
26386 this.timeout = setTimeout(function () {
26387 if (_t.hoverState == 'in') {
26390 }, this.delay.show);
26394 clearTimeout(this.timeout);
26396 this.hoverState = 'out';
26397 if (!this.delay || !this.delay.hide) {
26403 this.timeout = setTimeout(function () {
26404 //Roo.log("leave - timeout");
26406 if (_t.hoverState == 'out') {
26408 Roo.bootstrap.Tooltip.currentEl = false;
26413 show : function (msg)
26416 this.render(document.body);
26419 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26421 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26423 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26425 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26427 var placement = typeof this.placement == 'function' ?
26428 this.placement.call(this, this.el, on_el) :
26431 var autoToken = /\s?auto?\s?/i;
26432 var autoPlace = autoToken.test(placement);
26434 placement = placement.replace(autoToken, '') || 'top';
26438 //this.el.setXY([0,0]);
26440 //this.el.dom.style.display='block';
26442 //this.el.appendTo(on_el);
26444 var p = this.getPosition();
26445 var box = this.el.getBox();
26451 var align = this.alignment[placement];
26453 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26455 if(placement == 'top' || placement == 'bottom'){
26457 placement = 'right';
26460 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26461 placement = 'left';
26464 var scroll = Roo.select('body', true).first().getScroll();
26466 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26470 align = this.alignment[placement];
26473 this.el.alignTo(this.bindEl, align[0],align[1]);
26474 //var arrow = this.el.select('.arrow',true).first();
26475 //arrow.set(align[2],
26477 this.el.addClass(placement);
26479 this.el.addClass('in fade');
26481 this.hoverState = null;
26483 if (this.el.hasClass('fade')) {
26494 //this.el.setXY([0,0]);
26495 this.el.removeClass('in');
26511 * @class Roo.bootstrap.LocationPicker
26512 * @extends Roo.bootstrap.Component
26513 * Bootstrap LocationPicker class
26514 * @cfg {Number} latitude Position when init default 0
26515 * @cfg {Number} longitude Position when init default 0
26516 * @cfg {Number} zoom default 15
26517 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26518 * @cfg {Boolean} mapTypeControl default false
26519 * @cfg {Boolean} disableDoubleClickZoom default false
26520 * @cfg {Boolean} scrollwheel default true
26521 * @cfg {Boolean} streetViewControl default false
26522 * @cfg {Number} radius default 0
26523 * @cfg {String} locationName
26524 * @cfg {Boolean} draggable default true
26525 * @cfg {Boolean} enableAutocomplete default false
26526 * @cfg {Boolean} enableReverseGeocode default true
26527 * @cfg {String} markerTitle
26530 * Create a new LocationPicker
26531 * @param {Object} config The config object
26535 Roo.bootstrap.LocationPicker = function(config){
26537 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26542 * Fires when the picker initialized.
26543 * @param {Roo.bootstrap.LocationPicker} this
26544 * @param {Google Location} location
26548 * @event positionchanged
26549 * Fires when the picker position changed.
26550 * @param {Roo.bootstrap.LocationPicker} this
26551 * @param {Google Location} location
26553 positionchanged : true,
26556 * Fires when the map resize.
26557 * @param {Roo.bootstrap.LocationPicker} this
26562 * Fires when the map show.
26563 * @param {Roo.bootstrap.LocationPicker} this
26568 * Fires when the map hide.
26569 * @param {Roo.bootstrap.LocationPicker} this
26574 * Fires when click the map.
26575 * @param {Roo.bootstrap.LocationPicker} this
26576 * @param {Map event} e
26580 * @event mapRightClick
26581 * Fires when right click the map.
26582 * @param {Roo.bootstrap.LocationPicker} this
26583 * @param {Map event} e
26585 mapRightClick : true,
26587 * @event markerClick
26588 * Fires when click the marker.
26589 * @param {Roo.bootstrap.LocationPicker} this
26590 * @param {Map event} e
26592 markerClick : true,
26594 * @event markerRightClick
26595 * Fires when right click the marker.
26596 * @param {Roo.bootstrap.LocationPicker} this
26597 * @param {Map event} e
26599 markerRightClick : true,
26601 * @event OverlayViewDraw
26602 * Fires when OverlayView Draw
26603 * @param {Roo.bootstrap.LocationPicker} this
26605 OverlayViewDraw : true,
26607 * @event OverlayViewOnAdd
26608 * Fires when OverlayView Draw
26609 * @param {Roo.bootstrap.LocationPicker} this
26611 OverlayViewOnAdd : true,
26613 * @event OverlayViewOnRemove
26614 * Fires when OverlayView Draw
26615 * @param {Roo.bootstrap.LocationPicker} this
26617 OverlayViewOnRemove : true,
26619 * @event OverlayViewShow
26620 * Fires when OverlayView Draw
26621 * @param {Roo.bootstrap.LocationPicker} this
26622 * @param {Pixel} cpx
26624 OverlayViewShow : true,
26626 * @event OverlayViewHide
26627 * Fires when OverlayView Draw
26628 * @param {Roo.bootstrap.LocationPicker} this
26630 OverlayViewHide : true,
26632 * @event loadexception
26633 * Fires when load google lib failed.
26634 * @param {Roo.bootstrap.LocationPicker} this
26636 loadexception : true
26641 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26643 gMapContext: false,
26649 mapTypeControl: false,
26650 disableDoubleClickZoom: false,
26652 streetViewControl: false,
26656 enableAutocomplete: false,
26657 enableReverseGeocode: true,
26660 getAutoCreate: function()
26665 cls: 'roo-location-picker'
26671 initEvents: function(ct, position)
26673 if(!this.el.getWidth() || this.isApplied()){
26677 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26682 initial: function()
26684 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26685 this.fireEvent('loadexception', this);
26689 if(!this.mapTypeId){
26690 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26693 this.gMapContext = this.GMapContext();
26695 this.initOverlayView();
26697 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26701 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26702 _this.setPosition(_this.gMapContext.marker.position);
26705 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26706 _this.fireEvent('mapClick', this, event);
26710 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26711 _this.fireEvent('mapRightClick', this, event);
26715 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26716 _this.fireEvent('markerClick', this, event);
26720 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26721 _this.fireEvent('markerRightClick', this, event);
26725 this.setPosition(this.gMapContext.location);
26727 this.fireEvent('initial', this, this.gMapContext.location);
26730 initOverlayView: function()
26734 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26738 _this.fireEvent('OverlayViewDraw', _this);
26743 _this.fireEvent('OverlayViewOnAdd', _this);
26746 onRemove: function()
26748 _this.fireEvent('OverlayViewOnRemove', _this);
26751 show: function(cpx)
26753 _this.fireEvent('OverlayViewShow', _this, cpx);
26758 _this.fireEvent('OverlayViewHide', _this);
26764 fromLatLngToContainerPixel: function(event)
26766 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26769 isApplied: function()
26771 return this.getGmapContext() == false ? false : true;
26774 getGmapContext: function()
26776 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26779 GMapContext: function()
26781 var position = new google.maps.LatLng(this.latitude, this.longitude);
26783 var _map = new google.maps.Map(this.el.dom, {
26786 mapTypeId: this.mapTypeId,
26787 mapTypeControl: this.mapTypeControl,
26788 disableDoubleClickZoom: this.disableDoubleClickZoom,
26789 scrollwheel: this.scrollwheel,
26790 streetViewControl: this.streetViewControl,
26791 locationName: this.locationName,
26792 draggable: this.draggable,
26793 enableAutocomplete: this.enableAutocomplete,
26794 enableReverseGeocode: this.enableReverseGeocode
26797 var _marker = new google.maps.Marker({
26798 position: position,
26800 title: this.markerTitle,
26801 draggable: this.draggable
26808 location: position,
26809 radius: this.radius,
26810 locationName: this.locationName,
26811 addressComponents: {
26812 formatted_address: null,
26813 addressLine1: null,
26814 addressLine2: null,
26816 streetNumber: null,
26820 stateOrProvince: null
26823 domContainer: this.el.dom,
26824 geodecoder: new google.maps.Geocoder()
26828 drawCircle: function(center, radius, options)
26830 if (this.gMapContext.circle != null) {
26831 this.gMapContext.circle.setMap(null);
26835 options = Roo.apply({}, options, {
26836 strokeColor: "#0000FF",
26837 strokeOpacity: .35,
26839 fillColor: "#0000FF",
26843 options.map = this.gMapContext.map;
26844 options.radius = radius;
26845 options.center = center;
26846 this.gMapContext.circle = new google.maps.Circle(options);
26847 return this.gMapContext.circle;
26853 setPosition: function(location)
26855 this.gMapContext.location = location;
26856 this.gMapContext.marker.setPosition(location);
26857 this.gMapContext.map.panTo(location);
26858 this.drawCircle(location, this.gMapContext.radius, {});
26862 if (this.gMapContext.settings.enableReverseGeocode) {
26863 this.gMapContext.geodecoder.geocode({
26864 latLng: this.gMapContext.location
26865 }, function(results, status) {
26867 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26868 _this.gMapContext.locationName = results[0].formatted_address;
26869 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26871 _this.fireEvent('positionchanged', this, location);
26878 this.fireEvent('positionchanged', this, location);
26883 google.maps.event.trigger(this.gMapContext.map, "resize");
26885 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26887 this.fireEvent('resize', this);
26890 setPositionByLatLng: function(latitude, longitude)
26892 this.setPosition(new google.maps.LatLng(latitude, longitude));
26895 getCurrentPosition: function()
26898 latitude: this.gMapContext.location.lat(),
26899 longitude: this.gMapContext.location.lng()
26903 getAddressName: function()
26905 return this.gMapContext.locationName;
26908 getAddressComponents: function()
26910 return this.gMapContext.addressComponents;
26913 address_component_from_google_geocode: function(address_components)
26917 for (var i = 0; i < address_components.length; i++) {
26918 var component = address_components[i];
26919 if (component.types.indexOf("postal_code") >= 0) {
26920 result.postalCode = component.short_name;
26921 } else if (component.types.indexOf("street_number") >= 0) {
26922 result.streetNumber = component.short_name;
26923 } else if (component.types.indexOf("route") >= 0) {
26924 result.streetName = component.short_name;
26925 } else if (component.types.indexOf("neighborhood") >= 0) {
26926 result.city = component.short_name;
26927 } else if (component.types.indexOf("locality") >= 0) {
26928 result.city = component.short_name;
26929 } else if (component.types.indexOf("sublocality") >= 0) {
26930 result.district = component.short_name;
26931 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26932 result.stateOrProvince = component.short_name;
26933 } else if (component.types.indexOf("country") >= 0) {
26934 result.country = component.short_name;
26938 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26939 result.addressLine2 = "";
26943 setZoomLevel: function(zoom)
26945 this.gMapContext.map.setZoom(zoom);
26958 this.fireEvent('show', this);
26969 this.fireEvent('hide', this);
26974 Roo.apply(Roo.bootstrap.LocationPicker, {
26976 OverlayView : function(map, options)
26978 options = options || {};
26992 * @class Roo.bootstrap.Alert
26993 * @extends Roo.bootstrap.Component
26994 * Bootstrap Alert class
26995 * @cfg {String} title The title of alert
26996 * @cfg {String} html The content of alert
26997 * @cfg {String} weight ( success | info | warning | danger )
26998 * @cfg {String} faicon font-awesomeicon
27001 * Create a new alert
27002 * @param {Object} config The config object
27006 Roo.bootstrap.Alert = function(config){
27007 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27011 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27018 getAutoCreate : function()
27027 cls : 'roo-alert-icon'
27032 cls : 'roo-alert-title',
27037 cls : 'roo-alert-text',
27044 cfg.cn[0].cls += ' fa ' + this.faicon;
27048 cfg.cls += ' alert-' + this.weight;
27054 initEvents: function()
27056 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27059 setTitle : function(str)
27061 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27064 setText : function(str)
27066 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27069 setWeight : function(weight)
27072 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27075 this.weight = weight;
27077 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27080 setIcon : function(icon)
27083 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27086 this.faicon = icon;
27088 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27109 * @class Roo.bootstrap.UploadCropbox
27110 * @extends Roo.bootstrap.Component
27111 * Bootstrap UploadCropbox class
27112 * @cfg {String} emptyText show when image has been loaded
27113 * @cfg {String} rotateNotify show when image too small to rotate
27114 * @cfg {Number} errorTimeout default 3000
27115 * @cfg {Number} minWidth default 300
27116 * @cfg {Number} minHeight default 300
27117 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27118 * @cfg {Boolean} isDocument (true|false) default false
27119 * @cfg {String} url action url
27120 * @cfg {String} paramName default 'imageUpload'
27121 * @cfg {String} method default POST
27122 * @cfg {Boolean} loadMask (true|false) default true
27123 * @cfg {Boolean} loadingText default 'Loading...'
27126 * Create a new UploadCropbox
27127 * @param {Object} config The config object
27130 Roo.bootstrap.UploadCropbox = function(config){
27131 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27135 * @event beforeselectfile
27136 * Fire before select file
27137 * @param {Roo.bootstrap.UploadCropbox} this
27139 "beforeselectfile" : true,
27142 * Fire after initEvent
27143 * @param {Roo.bootstrap.UploadCropbox} this
27148 * Fire after initEvent
27149 * @param {Roo.bootstrap.UploadCropbox} this
27150 * @param {String} data
27155 * Fire when preparing the file data
27156 * @param {Roo.bootstrap.UploadCropbox} this
27157 * @param {Object} file
27162 * Fire when get exception
27163 * @param {Roo.bootstrap.UploadCropbox} this
27164 * @param {XMLHttpRequest} xhr
27166 "exception" : true,
27168 * @event beforeloadcanvas
27169 * Fire before load the canvas
27170 * @param {Roo.bootstrap.UploadCropbox} this
27171 * @param {String} src
27173 "beforeloadcanvas" : true,
27176 * Fire when trash image
27177 * @param {Roo.bootstrap.UploadCropbox} this
27182 * Fire when download the image
27183 * @param {Roo.bootstrap.UploadCropbox} this
27187 * @event footerbuttonclick
27188 * Fire when footerbuttonclick
27189 * @param {Roo.bootstrap.UploadCropbox} this
27190 * @param {String} type
27192 "footerbuttonclick" : true,
27196 * @param {Roo.bootstrap.UploadCropbox} this
27201 * Fire when rotate the image
27202 * @param {Roo.bootstrap.UploadCropbox} this
27203 * @param {String} pos
27208 * Fire when inspect the file
27209 * @param {Roo.bootstrap.UploadCropbox} this
27210 * @param {Object} file
27215 * Fire when xhr upload the file
27216 * @param {Roo.bootstrap.UploadCropbox} this
27217 * @param {Object} data
27222 * Fire when arrange the file data
27223 * @param {Roo.bootstrap.UploadCropbox} this
27224 * @param {Object} formData
27229 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27232 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27234 emptyText : 'Click to upload image',
27235 rotateNotify : 'Image is too small to rotate',
27236 errorTimeout : 3000,
27250 cropType : 'image/jpeg',
27252 canvasLoaded : false,
27253 isDocument : false,
27255 paramName : 'imageUpload',
27257 loadingText : 'Loading...',
27260 getAutoCreate : function()
27264 cls : 'roo-upload-cropbox',
27268 cls : 'roo-upload-cropbox-selector',
27273 cls : 'roo-upload-cropbox-body',
27274 style : 'cursor:pointer',
27278 cls : 'roo-upload-cropbox-preview'
27282 cls : 'roo-upload-cropbox-thumb'
27286 cls : 'roo-upload-cropbox-empty-notify',
27287 html : this.emptyText
27291 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27292 html : this.rotateNotify
27298 cls : 'roo-upload-cropbox-footer',
27301 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27311 onRender : function(ct, position)
27313 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27315 if (this.buttons.length) {
27317 Roo.each(this.buttons, function(bb) {
27319 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27321 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27327 this.maskEl = this.el;
27331 initEvents : function()
27333 this.urlAPI = (window.createObjectURL && window) ||
27334 (window.URL && URL.revokeObjectURL && URL) ||
27335 (window.webkitURL && webkitURL);
27337 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27338 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27340 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27341 this.selectorEl.hide();
27343 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27344 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27346 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27347 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27348 this.thumbEl.hide();
27350 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27351 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27353 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27354 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27355 this.errorEl.hide();
27357 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27358 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27359 this.footerEl.hide();
27361 this.setThumbBoxSize();
27367 this.fireEvent('initial', this);
27374 window.addEventListener("resize", function() { _this.resize(); } );
27376 this.bodyEl.on('click', this.beforeSelectFile, this);
27379 this.bodyEl.on('touchstart', this.onTouchStart, this);
27380 this.bodyEl.on('touchmove', this.onTouchMove, this);
27381 this.bodyEl.on('touchend', this.onTouchEnd, this);
27385 this.bodyEl.on('mousedown', this.onMouseDown, this);
27386 this.bodyEl.on('mousemove', this.onMouseMove, this);
27387 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27388 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27389 Roo.get(document).on('mouseup', this.onMouseUp, this);
27392 this.selectorEl.on('change', this.onFileSelected, this);
27398 this.baseScale = 1;
27400 this.baseRotate = 1;
27401 this.dragable = false;
27402 this.pinching = false;
27405 this.cropData = false;
27406 this.notifyEl.dom.innerHTML = this.emptyText;
27408 this.selectorEl.dom.value = '';
27412 resize : function()
27414 if(this.fireEvent('resize', this) != false){
27415 this.setThumbBoxPosition();
27416 this.setCanvasPosition();
27420 onFooterButtonClick : function(e, el, o, type)
27423 case 'rotate-left' :
27424 this.onRotateLeft(e);
27426 case 'rotate-right' :
27427 this.onRotateRight(e);
27430 this.beforeSelectFile(e);
27445 this.fireEvent('footerbuttonclick', this, type);
27448 beforeSelectFile : function(e)
27450 e.preventDefault();
27452 if(this.fireEvent('beforeselectfile', this) != false){
27453 this.selectorEl.dom.click();
27457 onFileSelected : function(e)
27459 e.preventDefault();
27461 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27465 var file = this.selectorEl.dom.files[0];
27467 if(this.fireEvent('inspect', this, file) != false){
27468 this.prepare(file);
27473 trash : function(e)
27475 this.fireEvent('trash', this);
27478 download : function(e)
27480 this.fireEvent('download', this);
27483 loadCanvas : function(src)
27485 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27489 this.imageEl = document.createElement('img');
27493 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27495 this.imageEl.src = src;
27499 onLoadCanvas : function()
27501 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27502 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27504 this.bodyEl.un('click', this.beforeSelectFile, this);
27506 this.notifyEl.hide();
27507 this.thumbEl.show();
27508 this.footerEl.show();
27510 this.baseRotateLevel();
27512 if(this.isDocument){
27513 this.setThumbBoxSize();
27516 this.setThumbBoxPosition();
27518 this.baseScaleLevel();
27524 this.canvasLoaded = true;
27527 this.maskEl.unmask();
27532 setCanvasPosition : function()
27534 if(!this.canvasEl){
27538 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27539 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27541 this.previewEl.setLeft(pw);
27542 this.previewEl.setTop(ph);
27546 onMouseDown : function(e)
27550 this.dragable = true;
27551 this.pinching = false;
27553 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27554 this.dragable = false;
27558 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27559 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27563 onMouseMove : function(e)
27567 if(!this.canvasLoaded){
27571 if (!this.dragable){
27575 var minX = Math.ceil(this.thumbEl.getLeft(true));
27576 var minY = Math.ceil(this.thumbEl.getTop(true));
27578 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27579 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27581 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27582 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27584 x = x - this.mouseX;
27585 y = y - this.mouseY;
27587 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27588 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27590 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27591 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27593 this.previewEl.setLeft(bgX);
27594 this.previewEl.setTop(bgY);
27596 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27597 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27600 onMouseUp : function(e)
27604 this.dragable = false;
27607 onMouseWheel : function(e)
27611 this.startScale = this.scale;
27613 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27615 if(!this.zoomable()){
27616 this.scale = this.startScale;
27625 zoomable : function()
27627 var minScale = this.thumbEl.getWidth() / this.minWidth;
27629 if(this.minWidth < this.minHeight){
27630 minScale = this.thumbEl.getHeight() / this.minHeight;
27633 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27634 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27638 (this.rotate == 0 || this.rotate == 180) &&
27640 width > this.imageEl.OriginWidth ||
27641 height > this.imageEl.OriginHeight ||
27642 (width < this.minWidth && height < this.minHeight)
27650 (this.rotate == 90 || this.rotate == 270) &&
27652 width > this.imageEl.OriginWidth ||
27653 height > this.imageEl.OriginHeight ||
27654 (width < this.minHeight && height < this.minWidth)
27661 !this.isDocument &&
27662 (this.rotate == 0 || this.rotate == 180) &&
27664 width < this.minWidth ||
27665 width > this.imageEl.OriginWidth ||
27666 height < this.minHeight ||
27667 height > this.imageEl.OriginHeight
27674 !this.isDocument &&
27675 (this.rotate == 90 || this.rotate == 270) &&
27677 width < this.minHeight ||
27678 width > this.imageEl.OriginWidth ||
27679 height < this.minWidth ||
27680 height > this.imageEl.OriginHeight
27690 onRotateLeft : function(e)
27692 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27694 var minScale = this.thumbEl.getWidth() / this.minWidth;
27696 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27697 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27699 this.startScale = this.scale;
27701 while (this.getScaleLevel() < minScale){
27703 this.scale = this.scale + 1;
27705 if(!this.zoomable()){
27710 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27711 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27716 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27723 this.scale = this.startScale;
27725 this.onRotateFail();
27730 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27732 if(this.isDocument){
27733 this.setThumbBoxSize();
27734 this.setThumbBoxPosition();
27735 this.setCanvasPosition();
27740 this.fireEvent('rotate', this, 'left');
27744 onRotateRight : function(e)
27746 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27748 var minScale = this.thumbEl.getWidth() / this.minWidth;
27750 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27751 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27753 this.startScale = this.scale;
27755 while (this.getScaleLevel() < minScale){
27757 this.scale = this.scale + 1;
27759 if(!this.zoomable()){
27764 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27765 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27770 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27777 this.scale = this.startScale;
27779 this.onRotateFail();
27784 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27786 if(this.isDocument){
27787 this.setThumbBoxSize();
27788 this.setThumbBoxPosition();
27789 this.setCanvasPosition();
27794 this.fireEvent('rotate', this, 'right');
27797 onRotateFail : function()
27799 this.errorEl.show(true);
27803 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27808 this.previewEl.dom.innerHTML = '';
27810 var canvasEl = document.createElement("canvas");
27812 var contextEl = canvasEl.getContext("2d");
27814 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27815 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27816 var center = this.imageEl.OriginWidth / 2;
27818 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27819 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27820 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27821 center = this.imageEl.OriginHeight / 2;
27824 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27826 contextEl.translate(center, center);
27827 contextEl.rotate(this.rotate * Math.PI / 180);
27829 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27831 this.canvasEl = document.createElement("canvas");
27833 this.contextEl = this.canvasEl.getContext("2d");
27835 switch (this.rotate) {
27838 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27839 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27841 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27846 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27847 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27849 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27850 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);
27854 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27859 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27860 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27862 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27863 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);
27867 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);
27872 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27873 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27875 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27876 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27880 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);
27887 this.previewEl.appendChild(this.canvasEl);
27889 this.setCanvasPosition();
27894 if(!this.canvasLoaded){
27898 var imageCanvas = document.createElement("canvas");
27900 var imageContext = imageCanvas.getContext("2d");
27902 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27903 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27905 var center = imageCanvas.width / 2;
27907 imageContext.translate(center, center);
27909 imageContext.rotate(this.rotate * Math.PI / 180);
27911 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27913 var canvas = document.createElement("canvas");
27915 var context = canvas.getContext("2d");
27917 canvas.width = this.minWidth;
27918 canvas.height = this.minHeight;
27920 switch (this.rotate) {
27923 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27924 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27926 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27927 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27929 var targetWidth = this.minWidth - 2 * x;
27930 var targetHeight = this.minHeight - 2 * y;
27934 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27935 scale = targetWidth / width;
27938 if(x > 0 && y == 0){
27939 scale = targetHeight / height;
27942 if(x > 0 && y > 0){
27943 scale = targetWidth / width;
27945 if(width < height){
27946 scale = targetHeight / height;
27950 context.scale(scale, scale);
27952 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27953 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27955 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27956 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27958 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27963 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27964 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27966 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27967 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27969 var targetWidth = this.minWidth - 2 * x;
27970 var targetHeight = this.minHeight - 2 * y;
27974 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27975 scale = targetWidth / width;
27978 if(x > 0 && y == 0){
27979 scale = targetHeight / height;
27982 if(x > 0 && y > 0){
27983 scale = targetWidth / width;
27985 if(width < height){
27986 scale = targetHeight / height;
27990 context.scale(scale, scale);
27992 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27993 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27995 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27996 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27998 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28000 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28005 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28006 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28008 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28009 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28011 var targetWidth = this.minWidth - 2 * x;
28012 var targetHeight = this.minHeight - 2 * y;
28016 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28017 scale = targetWidth / width;
28020 if(x > 0 && y == 0){
28021 scale = targetHeight / height;
28024 if(x > 0 && y > 0){
28025 scale = targetWidth / width;
28027 if(width < height){
28028 scale = targetHeight / height;
28032 context.scale(scale, scale);
28034 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28035 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28037 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28038 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28040 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28041 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28043 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28048 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28049 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28051 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28052 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28054 var targetWidth = this.minWidth - 2 * x;
28055 var targetHeight = this.minHeight - 2 * y;
28059 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28060 scale = targetWidth / width;
28063 if(x > 0 && y == 0){
28064 scale = targetHeight / height;
28067 if(x > 0 && y > 0){
28068 scale = targetWidth / width;
28070 if(width < height){
28071 scale = targetHeight / height;
28075 context.scale(scale, scale);
28077 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28078 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28080 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28081 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28083 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28085 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28092 this.cropData = canvas.toDataURL(this.cropType);
28094 if(this.fireEvent('crop', this, this.cropData) !== false){
28095 this.process(this.file, this.cropData);
28102 setThumbBoxSize : function()
28106 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28107 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28108 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28110 this.minWidth = width;
28111 this.minHeight = height;
28113 if(this.rotate == 90 || this.rotate == 270){
28114 this.minWidth = height;
28115 this.minHeight = width;
28120 width = Math.ceil(this.minWidth * height / this.minHeight);
28122 if(this.minWidth > this.minHeight){
28124 height = Math.ceil(this.minHeight * width / this.minWidth);
28127 this.thumbEl.setStyle({
28128 width : width + 'px',
28129 height : height + 'px'
28136 setThumbBoxPosition : function()
28138 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28139 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28141 this.thumbEl.setLeft(x);
28142 this.thumbEl.setTop(y);
28146 baseRotateLevel : function()
28148 this.baseRotate = 1;
28151 typeof(this.exif) != 'undefined' &&
28152 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28153 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28155 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28158 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28162 baseScaleLevel : function()
28166 if(this.isDocument){
28168 if(this.baseRotate == 6 || this.baseRotate == 8){
28170 height = this.thumbEl.getHeight();
28171 this.baseScale = height / this.imageEl.OriginWidth;
28173 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28174 width = this.thumbEl.getWidth();
28175 this.baseScale = width / this.imageEl.OriginHeight;
28181 height = this.thumbEl.getHeight();
28182 this.baseScale = height / this.imageEl.OriginHeight;
28184 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28185 width = this.thumbEl.getWidth();
28186 this.baseScale = width / this.imageEl.OriginWidth;
28192 if(this.baseRotate == 6 || this.baseRotate == 8){
28194 width = this.thumbEl.getHeight();
28195 this.baseScale = width / this.imageEl.OriginHeight;
28197 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28198 height = this.thumbEl.getWidth();
28199 this.baseScale = height / this.imageEl.OriginHeight;
28202 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28203 height = this.thumbEl.getWidth();
28204 this.baseScale = height / this.imageEl.OriginHeight;
28206 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28207 width = this.thumbEl.getHeight();
28208 this.baseScale = width / this.imageEl.OriginWidth;
28215 width = this.thumbEl.getWidth();
28216 this.baseScale = width / this.imageEl.OriginWidth;
28218 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28219 height = this.thumbEl.getHeight();
28220 this.baseScale = height / this.imageEl.OriginHeight;
28223 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28225 height = this.thumbEl.getHeight();
28226 this.baseScale = height / this.imageEl.OriginHeight;
28228 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28229 width = this.thumbEl.getWidth();
28230 this.baseScale = width / this.imageEl.OriginWidth;
28238 getScaleLevel : function()
28240 return this.baseScale * Math.pow(1.1, this.scale);
28243 onTouchStart : function(e)
28245 if(!this.canvasLoaded){
28246 this.beforeSelectFile(e);
28250 var touches = e.browserEvent.touches;
28256 if(touches.length == 1){
28257 this.onMouseDown(e);
28261 if(touches.length != 2){
28267 for(var i = 0, finger; finger = touches[i]; i++){
28268 coords.push(finger.pageX, finger.pageY);
28271 var x = Math.pow(coords[0] - coords[2], 2);
28272 var y = Math.pow(coords[1] - coords[3], 2);
28274 this.startDistance = Math.sqrt(x + y);
28276 this.startScale = this.scale;
28278 this.pinching = true;
28279 this.dragable = false;
28283 onTouchMove : function(e)
28285 if(!this.pinching && !this.dragable){
28289 var touches = e.browserEvent.touches;
28296 this.onMouseMove(e);
28302 for(var i = 0, finger; finger = touches[i]; i++){
28303 coords.push(finger.pageX, finger.pageY);
28306 var x = Math.pow(coords[0] - coords[2], 2);
28307 var y = Math.pow(coords[1] - coords[3], 2);
28309 this.endDistance = Math.sqrt(x + y);
28311 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28313 if(!this.zoomable()){
28314 this.scale = this.startScale;
28322 onTouchEnd : function(e)
28324 this.pinching = false;
28325 this.dragable = false;
28329 process : function(file, crop)
28332 this.maskEl.mask(this.loadingText);
28335 this.xhr = new XMLHttpRequest();
28337 file.xhr = this.xhr;
28339 this.xhr.open(this.method, this.url, true);
28342 "Accept": "application/json",
28343 "Cache-Control": "no-cache",
28344 "X-Requested-With": "XMLHttpRequest"
28347 for (var headerName in headers) {
28348 var headerValue = headers[headerName];
28350 this.xhr.setRequestHeader(headerName, headerValue);
28356 this.xhr.onload = function()
28358 _this.xhrOnLoad(_this.xhr);
28361 this.xhr.onerror = function()
28363 _this.xhrOnError(_this.xhr);
28366 var formData = new FormData();
28368 formData.append('returnHTML', 'NO');
28371 formData.append('crop', crop);
28374 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28375 formData.append(this.paramName, file, file.name);
28378 if(typeof(file.filename) != 'undefined'){
28379 formData.append('filename', file.filename);
28382 if(typeof(file.mimetype) != 'undefined'){
28383 formData.append('mimetype', file.mimetype);
28386 if(this.fireEvent('arrange', this, formData) != false){
28387 this.xhr.send(formData);
28391 xhrOnLoad : function(xhr)
28394 this.maskEl.unmask();
28397 if (xhr.readyState !== 4) {
28398 this.fireEvent('exception', this, xhr);
28402 var response = Roo.decode(xhr.responseText);
28404 if(!response.success){
28405 this.fireEvent('exception', this, xhr);
28409 var response = Roo.decode(xhr.responseText);
28411 this.fireEvent('upload', this, response);
28415 xhrOnError : function()
28418 this.maskEl.unmask();
28421 Roo.log('xhr on error');
28423 var response = Roo.decode(xhr.responseText);
28429 prepare : function(file)
28432 this.maskEl.mask(this.loadingText);
28438 if(typeof(file) === 'string'){
28439 this.loadCanvas(file);
28443 if(!file || !this.urlAPI){
28448 this.cropType = file.type;
28452 if(this.fireEvent('prepare', this, this.file) != false){
28454 var reader = new FileReader();
28456 reader.onload = function (e) {
28457 if (e.target.error) {
28458 Roo.log(e.target.error);
28462 var buffer = e.target.result,
28463 dataView = new DataView(buffer),
28465 maxOffset = dataView.byteLength - 4,
28469 if (dataView.getUint16(0) === 0xffd8) {
28470 while (offset < maxOffset) {
28471 markerBytes = dataView.getUint16(offset);
28473 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28474 markerLength = dataView.getUint16(offset + 2) + 2;
28475 if (offset + markerLength > dataView.byteLength) {
28476 Roo.log('Invalid meta data: Invalid segment size.');
28480 if(markerBytes == 0xffe1){
28481 _this.parseExifData(
28488 offset += markerLength;
28498 var url = _this.urlAPI.createObjectURL(_this.file);
28500 _this.loadCanvas(url);
28505 reader.readAsArrayBuffer(this.file);
28511 parseExifData : function(dataView, offset, length)
28513 var tiffOffset = offset + 10,
28517 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28518 // No Exif data, might be XMP data instead
28522 // Check for the ASCII code for "Exif" (0x45786966):
28523 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28524 // No Exif data, might be XMP data instead
28527 if (tiffOffset + 8 > dataView.byteLength) {
28528 Roo.log('Invalid Exif data: Invalid segment size.');
28531 // Check for the two null bytes:
28532 if (dataView.getUint16(offset + 8) !== 0x0000) {
28533 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28536 // Check the byte alignment:
28537 switch (dataView.getUint16(tiffOffset)) {
28539 littleEndian = true;
28542 littleEndian = false;
28545 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28548 // Check for the TIFF tag marker (0x002A):
28549 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28550 Roo.log('Invalid Exif data: Missing TIFF marker.');
28553 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28554 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28556 this.parseExifTags(
28559 tiffOffset + dirOffset,
28564 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28569 if (dirOffset + 6 > dataView.byteLength) {
28570 Roo.log('Invalid Exif data: Invalid directory offset.');
28573 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28574 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28575 if (dirEndOffset + 4 > dataView.byteLength) {
28576 Roo.log('Invalid Exif data: Invalid directory size.');
28579 for (i = 0; i < tagsNumber; i += 1) {
28583 dirOffset + 2 + 12 * i, // tag offset
28587 // Return the offset to the next directory:
28588 return dataView.getUint32(dirEndOffset, littleEndian);
28591 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28593 var tag = dataView.getUint16(offset, littleEndian);
28595 this.exif[tag] = this.getExifValue(
28599 dataView.getUint16(offset + 2, littleEndian), // tag type
28600 dataView.getUint32(offset + 4, littleEndian), // tag length
28605 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28607 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28616 Roo.log('Invalid Exif data: Invalid tag type.');
28620 tagSize = tagType.size * length;
28621 // Determine if the value is contained in the dataOffset bytes,
28622 // or if the value at the dataOffset is a pointer to the actual data:
28623 dataOffset = tagSize > 4 ?
28624 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28625 if (dataOffset + tagSize > dataView.byteLength) {
28626 Roo.log('Invalid Exif data: Invalid data offset.');
28629 if (length === 1) {
28630 return tagType.getValue(dataView, dataOffset, littleEndian);
28633 for (i = 0; i < length; i += 1) {
28634 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28637 if (tagType.ascii) {
28639 // Concatenate the chars:
28640 for (i = 0; i < values.length; i += 1) {
28642 // Ignore the terminating NULL byte(s):
28643 if (c === '\u0000') {
28655 Roo.apply(Roo.bootstrap.UploadCropbox, {
28657 'Orientation': 0x0112
28661 1: 0, //'top-left',
28663 3: 180, //'bottom-right',
28664 // 4: 'bottom-left',
28666 6: 90, //'right-top',
28667 // 7: 'right-bottom',
28668 8: 270 //'left-bottom'
28672 // byte, 8-bit unsigned int:
28674 getValue: function (dataView, dataOffset) {
28675 return dataView.getUint8(dataOffset);
28679 // ascii, 8-bit byte:
28681 getValue: function (dataView, dataOffset) {
28682 return String.fromCharCode(dataView.getUint8(dataOffset));
28687 // short, 16 bit int:
28689 getValue: function (dataView, dataOffset, littleEndian) {
28690 return dataView.getUint16(dataOffset, littleEndian);
28694 // long, 32 bit int:
28696 getValue: function (dataView, dataOffset, littleEndian) {
28697 return dataView.getUint32(dataOffset, littleEndian);
28701 // rational = two long values, first is numerator, second is denominator:
28703 getValue: function (dataView, dataOffset, littleEndian) {
28704 return dataView.getUint32(dataOffset, littleEndian) /
28705 dataView.getUint32(dataOffset + 4, littleEndian);
28709 // slong, 32 bit signed int:
28711 getValue: function (dataView, dataOffset, littleEndian) {
28712 return dataView.getInt32(dataOffset, littleEndian);
28716 // srational, two slongs, first is numerator, second is denominator:
28718 getValue: function (dataView, dataOffset, littleEndian) {
28719 return dataView.getInt32(dataOffset, littleEndian) /
28720 dataView.getInt32(dataOffset + 4, littleEndian);
28730 cls : 'btn-group roo-upload-cropbox-rotate-left',
28731 action : 'rotate-left',
28735 cls : 'btn btn-default',
28736 html : '<i class="fa fa-undo"></i>'
28742 cls : 'btn-group roo-upload-cropbox-picture',
28743 action : 'picture',
28747 cls : 'btn btn-default',
28748 html : '<i class="fa fa-picture-o"></i>'
28754 cls : 'btn-group roo-upload-cropbox-rotate-right',
28755 action : 'rotate-right',
28759 cls : 'btn btn-default',
28760 html : '<i class="fa fa-repeat"></i>'
28768 cls : 'btn-group roo-upload-cropbox-rotate-left',
28769 action : 'rotate-left',
28773 cls : 'btn btn-default',
28774 html : '<i class="fa fa-undo"></i>'
28780 cls : 'btn-group roo-upload-cropbox-download',
28781 action : 'download',
28785 cls : 'btn btn-default',
28786 html : '<i class="fa fa-download"></i>'
28792 cls : 'btn-group roo-upload-cropbox-crop',
28797 cls : 'btn btn-default',
28798 html : '<i class="fa fa-crop"></i>'
28804 cls : 'btn-group roo-upload-cropbox-trash',
28809 cls : 'btn btn-default',
28810 html : '<i class="fa fa-trash"></i>'
28816 cls : 'btn-group roo-upload-cropbox-rotate-right',
28817 action : 'rotate-right',
28821 cls : 'btn btn-default',
28822 html : '<i class="fa fa-repeat"></i>'
28830 cls : 'btn-group roo-upload-cropbox-rotate-left',
28831 action : 'rotate-left',
28835 cls : 'btn btn-default',
28836 html : '<i class="fa fa-undo"></i>'
28842 cls : 'btn-group roo-upload-cropbox-rotate-right',
28843 action : 'rotate-right',
28847 cls : 'btn btn-default',
28848 html : '<i class="fa fa-repeat"></i>'
28861 * @class Roo.bootstrap.DocumentManager
28862 * @extends Roo.bootstrap.Component
28863 * Bootstrap DocumentManager class
28864 * @cfg {String} paramName default 'imageUpload'
28865 * @cfg {String} toolTipName default 'filename'
28866 * @cfg {String} method default POST
28867 * @cfg {String} url action url
28868 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28869 * @cfg {Boolean} multiple multiple upload default true
28870 * @cfg {Number} thumbSize default 300
28871 * @cfg {String} fieldLabel
28872 * @cfg {Number} labelWidth default 4
28873 * @cfg {String} labelAlign (left|top) default left
28874 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28875 * @cfg {Number} labellg set the width of label (1-12)
28876 * @cfg {Number} labelmd set the width of label (1-12)
28877 * @cfg {Number} labelsm set the width of label (1-12)
28878 * @cfg {Number} labelxs set the width of label (1-12)
28881 * Create a new DocumentManager
28882 * @param {Object} config The config object
28885 Roo.bootstrap.DocumentManager = function(config){
28886 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28889 this.delegates = [];
28894 * Fire when initial the DocumentManager
28895 * @param {Roo.bootstrap.DocumentManager} this
28900 * inspect selected file
28901 * @param {Roo.bootstrap.DocumentManager} this
28902 * @param {File} file
28907 * Fire when xhr load exception
28908 * @param {Roo.bootstrap.DocumentManager} this
28909 * @param {XMLHttpRequest} xhr
28911 "exception" : true,
28913 * @event afterupload
28914 * Fire when xhr load exception
28915 * @param {Roo.bootstrap.DocumentManager} this
28916 * @param {XMLHttpRequest} xhr
28918 "afterupload" : true,
28921 * prepare the form data
28922 * @param {Roo.bootstrap.DocumentManager} this
28923 * @param {Object} formData
28928 * Fire when remove the file
28929 * @param {Roo.bootstrap.DocumentManager} this
28930 * @param {Object} file
28935 * Fire after refresh the file
28936 * @param {Roo.bootstrap.DocumentManager} this
28941 * Fire after click the image
28942 * @param {Roo.bootstrap.DocumentManager} this
28943 * @param {Object} file
28948 * Fire when upload a image and editable set to true
28949 * @param {Roo.bootstrap.DocumentManager} this
28950 * @param {Object} file
28954 * @event beforeselectfile
28955 * Fire before select file
28956 * @param {Roo.bootstrap.DocumentManager} this
28958 "beforeselectfile" : true,
28961 * Fire before process file
28962 * @param {Roo.bootstrap.DocumentManager} this
28963 * @param {Object} file
28967 * @event previewrendered
28968 * Fire when preview rendered
28969 * @param {Roo.bootstrap.DocumentManager} this
28970 * @param {Object} file
28972 "previewrendered" : true,
28975 "previewResize" : true
28980 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28989 paramName : 'imageUpload',
28990 toolTipName : 'filename',
28993 labelAlign : 'left',
29003 getAutoCreate : function()
29005 var managerWidget = {
29007 cls : 'roo-document-manager',
29011 cls : 'roo-document-manager-selector',
29016 cls : 'roo-document-manager-uploader',
29020 cls : 'roo-document-manager-upload-btn',
29021 html : '<i class="fa fa-plus"></i>'
29032 cls : 'column col-md-12',
29037 if(this.fieldLabel.length){
29042 cls : 'column col-md-12',
29043 html : this.fieldLabel
29047 cls : 'column col-md-12',
29052 if(this.labelAlign == 'left'){
29057 html : this.fieldLabel
29066 if(this.labelWidth > 12){
29067 content[0].style = "width: " + this.labelWidth + 'px';
29070 if(this.labelWidth < 13 && this.labelmd == 0){
29071 this.labelmd = this.labelWidth;
29074 if(this.labellg > 0){
29075 content[0].cls += ' col-lg-' + this.labellg;
29076 content[1].cls += ' col-lg-' + (12 - this.labellg);
29079 if(this.labelmd > 0){
29080 content[0].cls += ' col-md-' + this.labelmd;
29081 content[1].cls += ' col-md-' + (12 - this.labelmd);
29084 if(this.labelsm > 0){
29085 content[0].cls += ' col-sm-' + this.labelsm;
29086 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29089 if(this.labelxs > 0){
29090 content[0].cls += ' col-xs-' + this.labelxs;
29091 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29099 cls : 'row clearfix',
29107 initEvents : function()
29109 this.managerEl = this.el.select('.roo-document-manager', true).first();
29110 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29112 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29113 this.selectorEl.hide();
29116 this.selectorEl.attr('multiple', 'multiple');
29119 this.selectorEl.on('change', this.onFileSelected, this);
29121 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29122 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29124 this.uploader.on('click', this.onUploaderClick, this);
29126 this.renderProgressDialog();
29130 window.addEventListener("resize", function() { _this.refresh(); } );
29132 this.fireEvent('initial', this);
29135 renderProgressDialog : function()
29139 this.progressDialog = new Roo.bootstrap.Modal({
29140 cls : 'roo-document-manager-progress-dialog',
29141 allow_close : false,
29151 btnclick : function() {
29152 _this.uploadCancel();
29158 this.progressDialog.render(Roo.get(document.body));
29160 this.progress = new Roo.bootstrap.Progress({
29161 cls : 'roo-document-manager-progress',
29166 this.progress.render(this.progressDialog.getChildContainer());
29168 this.progressBar = new Roo.bootstrap.ProgressBar({
29169 cls : 'roo-document-manager-progress-bar',
29172 aria_valuemax : 12,
29176 this.progressBar.render(this.progress.getChildContainer());
29179 onUploaderClick : function(e)
29181 e.preventDefault();
29183 if(this.fireEvent('beforeselectfile', this) != false){
29184 this.selectorEl.dom.click();
29189 onFileSelected : function(e)
29191 e.preventDefault();
29193 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29197 Roo.each(this.selectorEl.dom.files, function(file){
29198 if(this.fireEvent('inspect', this, file) != false){
29199 this.files.push(file);
29209 this.selectorEl.dom.value = '';
29211 if(!this.files || !this.files.length){
29215 if(this.boxes > 0 && this.files.length > this.boxes){
29216 this.files = this.files.slice(0, this.boxes);
29219 this.uploader.show();
29221 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29222 this.uploader.hide();
29231 Roo.each(this.files, function(file){
29233 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29234 var f = this.renderPreview(file);
29239 if(file.type.indexOf('image') != -1){
29240 this.delegates.push(
29242 _this.process(file);
29243 }).createDelegate(this)
29251 _this.process(file);
29252 }).createDelegate(this)
29257 this.files = files;
29259 this.delegates = this.delegates.concat(docs);
29261 if(!this.delegates.length){
29266 this.progressBar.aria_valuemax = this.delegates.length;
29273 arrange : function()
29275 if(!this.delegates.length){
29276 this.progressDialog.hide();
29281 var delegate = this.delegates.shift();
29283 this.progressDialog.show();
29285 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29287 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29292 refresh : function()
29294 this.uploader.show();
29296 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29297 this.uploader.hide();
29300 Roo.isTouch ? this.closable(false) : this.closable(true);
29302 this.fireEvent('refresh', this);
29305 onRemove : function(e, el, o)
29307 e.preventDefault();
29309 this.fireEvent('remove', this, o);
29313 remove : function(o)
29317 Roo.each(this.files, function(file){
29318 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29327 this.files = files;
29334 Roo.each(this.files, function(file){
29339 file.target.remove();
29348 onClick : function(e, el, o)
29350 e.preventDefault();
29352 this.fireEvent('click', this, o);
29356 closable : function(closable)
29358 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29360 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29372 xhrOnLoad : function(xhr)
29374 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29378 if (xhr.readyState !== 4) {
29380 this.fireEvent('exception', this, xhr);
29384 var response = Roo.decode(xhr.responseText);
29386 if(!response.success){
29388 this.fireEvent('exception', this, xhr);
29392 var file = this.renderPreview(response.data);
29394 this.files.push(file);
29398 this.fireEvent('afterupload', this, xhr);
29402 xhrOnError : function(xhr)
29404 Roo.log('xhr on error');
29406 var response = Roo.decode(xhr.responseText);
29413 process : function(file)
29415 if(this.fireEvent('process', this, file) !== false){
29416 if(this.editable && file.type.indexOf('image') != -1){
29417 this.fireEvent('edit', this, file);
29421 this.uploadStart(file, false);
29428 uploadStart : function(file, crop)
29430 this.xhr = new XMLHttpRequest();
29432 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29437 file.xhr = this.xhr;
29439 this.managerEl.createChild({
29441 cls : 'roo-document-manager-loading',
29445 tooltip : file.name,
29446 cls : 'roo-document-manager-thumb',
29447 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29453 this.xhr.open(this.method, this.url, true);
29456 "Accept": "application/json",
29457 "Cache-Control": "no-cache",
29458 "X-Requested-With": "XMLHttpRequest"
29461 for (var headerName in headers) {
29462 var headerValue = headers[headerName];
29464 this.xhr.setRequestHeader(headerName, headerValue);
29470 this.xhr.onload = function()
29472 _this.xhrOnLoad(_this.xhr);
29475 this.xhr.onerror = function()
29477 _this.xhrOnError(_this.xhr);
29480 var formData = new FormData();
29482 formData.append('returnHTML', 'NO');
29485 formData.append('crop', crop);
29488 formData.append(this.paramName, file, file.name);
29495 if(this.fireEvent('prepare', this, formData, options) != false){
29497 if(options.manually){
29501 this.xhr.send(formData);
29505 this.uploadCancel();
29508 uploadCancel : function()
29514 this.delegates = [];
29516 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29523 renderPreview : function(file)
29525 if(typeof(file.target) != 'undefined' && file.target){
29529 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29531 var previewEl = this.managerEl.createChild({
29533 cls : 'roo-document-manager-preview',
29537 tooltip : file[this.toolTipName],
29538 cls : 'roo-document-manager-thumb',
29539 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29544 html : '<i class="fa fa-times-circle"></i>'
29549 var close = previewEl.select('button.close', true).first();
29551 close.on('click', this.onRemove, this, file);
29553 file.target = previewEl;
29555 var image = previewEl.select('img', true).first();
29559 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29561 image.on('click', this.onClick, this, file);
29563 this.fireEvent('previewrendered', this, file);
29569 onPreviewLoad : function(file, image)
29571 if(typeof(file.target) == 'undefined' || !file.target){
29575 var width = image.dom.naturalWidth || image.dom.width;
29576 var height = image.dom.naturalHeight || image.dom.height;
29578 if(!this.previewResize) {
29582 if(width > height){
29583 file.target.addClass('wide');
29587 file.target.addClass('tall');
29592 uploadFromSource : function(file, crop)
29594 this.xhr = new XMLHttpRequest();
29596 this.managerEl.createChild({
29598 cls : 'roo-document-manager-loading',
29602 tooltip : file.name,
29603 cls : 'roo-document-manager-thumb',
29604 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29610 this.xhr.open(this.method, this.url, true);
29613 "Accept": "application/json",
29614 "Cache-Control": "no-cache",
29615 "X-Requested-With": "XMLHttpRequest"
29618 for (var headerName in headers) {
29619 var headerValue = headers[headerName];
29621 this.xhr.setRequestHeader(headerName, headerValue);
29627 this.xhr.onload = function()
29629 _this.xhrOnLoad(_this.xhr);
29632 this.xhr.onerror = function()
29634 _this.xhrOnError(_this.xhr);
29637 var formData = new FormData();
29639 formData.append('returnHTML', 'NO');
29641 formData.append('crop', crop);
29643 if(typeof(file.filename) != 'undefined'){
29644 formData.append('filename', file.filename);
29647 if(typeof(file.mimetype) != 'undefined'){
29648 formData.append('mimetype', file.mimetype);
29653 if(this.fireEvent('prepare', this, formData) != false){
29654 this.xhr.send(formData);
29664 * @class Roo.bootstrap.DocumentViewer
29665 * @extends Roo.bootstrap.Component
29666 * Bootstrap DocumentViewer class
29667 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29668 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29671 * Create a new DocumentViewer
29672 * @param {Object} config The config object
29675 Roo.bootstrap.DocumentViewer = function(config){
29676 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29681 * Fire after initEvent
29682 * @param {Roo.bootstrap.DocumentViewer} this
29688 * @param {Roo.bootstrap.DocumentViewer} this
29693 * Fire after download button
29694 * @param {Roo.bootstrap.DocumentViewer} this
29699 * Fire after trash button
29700 * @param {Roo.bootstrap.DocumentViewer} this
29707 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29709 showDownload : true,
29713 getAutoCreate : function()
29717 cls : 'roo-document-viewer',
29721 cls : 'roo-document-viewer-body',
29725 cls : 'roo-document-viewer-thumb',
29729 cls : 'roo-document-viewer-image'
29737 cls : 'roo-document-viewer-footer',
29740 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29744 cls : 'btn-group roo-document-viewer-download',
29748 cls : 'btn btn-default',
29749 html : '<i class="fa fa-download"></i>'
29755 cls : 'btn-group roo-document-viewer-trash',
29759 cls : 'btn btn-default',
29760 html : '<i class="fa fa-trash"></i>'
29773 initEvents : function()
29775 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29776 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29778 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29779 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29781 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29782 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29784 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29785 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29787 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29788 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29790 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29791 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29793 this.bodyEl.on('click', this.onClick, this);
29794 this.downloadBtn.on('click', this.onDownload, this);
29795 this.trashBtn.on('click', this.onTrash, this);
29797 this.downloadBtn.hide();
29798 this.trashBtn.hide();
29800 if(this.showDownload){
29801 this.downloadBtn.show();
29804 if(this.showTrash){
29805 this.trashBtn.show();
29808 if(!this.showDownload && !this.showTrash) {
29809 this.footerEl.hide();
29814 initial : function()
29816 this.fireEvent('initial', this);
29820 onClick : function(e)
29822 e.preventDefault();
29824 this.fireEvent('click', this);
29827 onDownload : function(e)
29829 e.preventDefault();
29831 this.fireEvent('download', this);
29834 onTrash : function(e)
29836 e.preventDefault();
29838 this.fireEvent('trash', this);
29850 * @class Roo.bootstrap.NavProgressBar
29851 * @extends Roo.bootstrap.Component
29852 * Bootstrap NavProgressBar class
29855 * Create a new nav progress bar
29856 * @param {Object} config The config object
29859 Roo.bootstrap.NavProgressBar = function(config){
29860 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29862 this.bullets = this.bullets || [];
29864 // Roo.bootstrap.NavProgressBar.register(this);
29868 * Fires when the active item changes
29869 * @param {Roo.bootstrap.NavProgressBar} this
29870 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29871 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29878 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29883 getAutoCreate : function()
29885 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29889 cls : 'roo-navigation-bar-group',
29893 cls : 'roo-navigation-top-bar'
29897 cls : 'roo-navigation-bullets-bar',
29901 cls : 'roo-navigation-bar'
29908 cls : 'roo-navigation-bottom-bar'
29918 initEvents: function()
29923 onRender : function(ct, position)
29925 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29927 if(this.bullets.length){
29928 Roo.each(this.bullets, function(b){
29937 addItem : function(cfg)
29939 var item = new Roo.bootstrap.NavProgressItem(cfg);
29941 item.parentId = this.id;
29942 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29945 var top = new Roo.bootstrap.Element({
29947 cls : 'roo-navigation-bar-text'
29950 var bottom = new Roo.bootstrap.Element({
29952 cls : 'roo-navigation-bar-text'
29955 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29956 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29958 var topText = new Roo.bootstrap.Element({
29960 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29963 var bottomText = new Roo.bootstrap.Element({
29965 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29968 topText.onRender(top.el, null);
29969 bottomText.onRender(bottom.el, null);
29972 item.bottomEl = bottom;
29975 this.barItems.push(item);
29980 getActive : function()
29982 var active = false;
29984 Roo.each(this.barItems, function(v){
29986 if (!v.isActive()) {
29998 setActiveItem : function(item)
30002 Roo.each(this.barItems, function(v){
30003 if (v.rid == item.rid) {
30007 if (v.isActive()) {
30008 v.setActive(false);
30013 item.setActive(true);
30015 this.fireEvent('changed', this, item, prev);
30018 getBarItem: function(rid)
30022 Roo.each(this.barItems, function(e) {
30023 if (e.rid != rid) {
30034 indexOfItem : function(item)
30038 Roo.each(this.barItems, function(v, i){
30040 if (v.rid != item.rid) {
30051 setActiveNext : function()
30053 var i = this.indexOfItem(this.getActive());
30055 if (i > this.barItems.length) {
30059 this.setActiveItem(this.barItems[i+1]);
30062 setActivePrev : function()
30064 var i = this.indexOfItem(this.getActive());
30070 this.setActiveItem(this.barItems[i-1]);
30073 format : function()
30075 if(!this.barItems.length){
30079 var width = 100 / this.barItems.length;
30081 Roo.each(this.barItems, function(i){
30082 i.el.setStyle('width', width + '%');
30083 i.topEl.el.setStyle('width', width + '%');
30084 i.bottomEl.el.setStyle('width', width + '%');
30093 * Nav Progress Item
30098 * @class Roo.bootstrap.NavProgressItem
30099 * @extends Roo.bootstrap.Component
30100 * Bootstrap NavProgressItem class
30101 * @cfg {String} rid the reference id
30102 * @cfg {Boolean} active (true|false) Is item active default false
30103 * @cfg {Boolean} disabled (true|false) Is item active default false
30104 * @cfg {String} html
30105 * @cfg {String} position (top|bottom) text position default bottom
30106 * @cfg {String} icon show icon instead of number
30109 * Create a new NavProgressItem
30110 * @param {Object} config The config object
30112 Roo.bootstrap.NavProgressItem = function(config){
30113 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30118 * The raw click event for the entire grid.
30119 * @param {Roo.bootstrap.NavProgressItem} this
30120 * @param {Roo.EventObject} e
30127 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30133 position : 'bottom',
30136 getAutoCreate : function()
30138 var iconCls = 'roo-navigation-bar-item-icon';
30140 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30144 cls: 'roo-navigation-bar-item',
30154 cfg.cls += ' active';
30157 cfg.cls += ' disabled';
30163 disable : function()
30165 this.setDisabled(true);
30168 enable : function()
30170 this.setDisabled(false);
30173 initEvents: function()
30175 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30177 this.iconEl.on('click', this.onClick, this);
30180 onClick : function(e)
30182 e.preventDefault();
30188 if(this.fireEvent('click', this, e) === false){
30192 this.parent().setActiveItem(this);
30195 isActive: function ()
30197 return this.active;
30200 setActive : function(state)
30202 if(this.active == state){
30206 this.active = state;
30209 this.el.addClass('active');
30213 this.el.removeClass('active');
30218 setDisabled : function(state)
30220 if(this.disabled == state){
30224 this.disabled = state;
30227 this.el.addClass('disabled');
30231 this.el.removeClass('disabled');
30234 tooltipEl : function()
30236 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30249 * @class Roo.bootstrap.FieldLabel
30250 * @extends Roo.bootstrap.Component
30251 * Bootstrap FieldLabel class
30252 * @cfg {String} html contents of the element
30253 * @cfg {String} tag tag of the element default label
30254 * @cfg {String} cls class of the element
30255 * @cfg {String} target label target
30256 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30257 * @cfg {String} invalidClass default "text-warning"
30258 * @cfg {String} validClass default "text-success"
30259 * @cfg {String} iconTooltip default "This field is required"
30260 * @cfg {String} indicatorpos (left|right) default left
30263 * Create a new FieldLabel
30264 * @param {Object} config The config object
30267 Roo.bootstrap.FieldLabel = function(config){
30268 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30273 * Fires after the field has been marked as invalid.
30274 * @param {Roo.form.FieldLabel} this
30275 * @param {String} msg The validation message
30280 * Fires after the field has been validated with no errors.
30281 * @param {Roo.form.FieldLabel} this
30287 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30294 invalidClass : 'has-warning',
30295 validClass : 'has-success',
30296 iconTooltip : 'This field is required',
30297 indicatorpos : 'left',
30299 getAutoCreate : function(){
30302 if (!this.allowBlank) {
30308 cls : 'roo-bootstrap-field-label ' + this.cls,
30313 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30314 tooltip : this.iconTooltip
30323 if(this.indicatorpos == 'right'){
30326 cls : 'roo-bootstrap-field-label ' + this.cls,
30335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30336 tooltip : this.iconTooltip
30345 initEvents: function()
30347 Roo.bootstrap.Element.superclass.initEvents.call(this);
30349 this.indicator = this.indicatorEl();
30351 if(this.indicator){
30352 this.indicator.removeClass('visible');
30353 this.indicator.addClass('invisible');
30356 Roo.bootstrap.FieldLabel.register(this);
30359 indicatorEl : function()
30361 var indicator = this.el.select('i.roo-required-indicator',true).first();
30372 * Mark this field as valid
30374 markValid : function()
30376 if(this.indicator){
30377 this.indicator.removeClass('visible');
30378 this.indicator.addClass('invisible');
30381 this.el.removeClass(this.invalidClass);
30383 this.el.addClass(this.validClass);
30385 this.fireEvent('valid', this);
30389 * Mark this field as invalid
30390 * @param {String} msg The validation message
30392 markInvalid : function(msg)
30394 if(this.indicator){
30395 this.indicator.removeClass('invisible');
30396 this.indicator.addClass('visible');
30399 this.el.removeClass(this.validClass);
30401 this.el.addClass(this.invalidClass);
30403 this.fireEvent('invalid', this, msg);
30409 Roo.apply(Roo.bootstrap.FieldLabel, {
30414 * register a FieldLabel Group
30415 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30417 register : function(label)
30419 if(this.groups.hasOwnProperty(label.target)){
30423 this.groups[label.target] = label;
30427 * fetch a FieldLabel Group based on the target
30428 * @param {string} target
30429 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30431 get: function(target) {
30432 if (typeof(this.groups[target]) == 'undefined') {
30436 return this.groups[target] ;
30445 * page DateSplitField.
30451 * @class Roo.bootstrap.DateSplitField
30452 * @extends Roo.bootstrap.Component
30453 * Bootstrap DateSplitField class
30454 * @cfg {string} fieldLabel - the label associated
30455 * @cfg {Number} labelWidth set the width of label (0-12)
30456 * @cfg {String} labelAlign (top|left)
30457 * @cfg {Boolean} dayAllowBlank (true|false) default false
30458 * @cfg {Boolean} monthAllowBlank (true|false) default false
30459 * @cfg {Boolean} yearAllowBlank (true|false) default false
30460 * @cfg {string} dayPlaceholder
30461 * @cfg {string} monthPlaceholder
30462 * @cfg {string} yearPlaceholder
30463 * @cfg {string} dayFormat default 'd'
30464 * @cfg {string} monthFormat default 'm'
30465 * @cfg {string} yearFormat default 'Y'
30466 * @cfg {Number} labellg set the width of label (1-12)
30467 * @cfg {Number} labelmd set the width of label (1-12)
30468 * @cfg {Number} labelsm set the width of label (1-12)
30469 * @cfg {Number} labelxs set the width of label (1-12)
30473 * Create a new DateSplitField
30474 * @param {Object} config The config object
30477 Roo.bootstrap.DateSplitField = function(config){
30478 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30484 * getting the data of years
30485 * @param {Roo.bootstrap.DateSplitField} this
30486 * @param {Object} years
30491 * getting the data of days
30492 * @param {Roo.bootstrap.DateSplitField} this
30493 * @param {Object} days
30498 * Fires after the field has been marked as invalid.
30499 * @param {Roo.form.Field} this
30500 * @param {String} msg The validation message
30505 * Fires after the field has been validated with no errors.
30506 * @param {Roo.form.Field} this
30512 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30515 labelAlign : 'top',
30517 dayAllowBlank : false,
30518 monthAllowBlank : false,
30519 yearAllowBlank : false,
30520 dayPlaceholder : '',
30521 monthPlaceholder : '',
30522 yearPlaceholder : '',
30526 isFormField : true,
30532 getAutoCreate : function()
30536 cls : 'row roo-date-split-field-group',
30541 cls : 'form-hidden-field roo-date-split-field-group-value',
30547 var labelCls = 'col-md-12';
30548 var contentCls = 'col-md-4';
30550 if(this.fieldLabel){
30554 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30558 html : this.fieldLabel
30563 if(this.labelAlign == 'left'){
30565 if(this.labelWidth > 12){
30566 label.style = "width: " + this.labelWidth + 'px';
30569 if(this.labelWidth < 13 && this.labelmd == 0){
30570 this.labelmd = this.labelWidth;
30573 if(this.labellg > 0){
30574 labelCls = ' col-lg-' + this.labellg;
30575 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30578 if(this.labelmd > 0){
30579 labelCls = ' col-md-' + this.labelmd;
30580 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30583 if(this.labelsm > 0){
30584 labelCls = ' col-sm-' + this.labelsm;
30585 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30588 if(this.labelxs > 0){
30589 labelCls = ' col-xs-' + this.labelxs;
30590 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30594 label.cls += ' ' + labelCls;
30596 cfg.cn.push(label);
30599 Roo.each(['day', 'month', 'year'], function(t){
30602 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30609 inputEl: function ()
30611 return this.el.select('.roo-date-split-field-group-value', true).first();
30614 onRender : function(ct, position)
30618 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30620 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30622 this.dayField = new Roo.bootstrap.ComboBox({
30623 allowBlank : this.dayAllowBlank,
30624 alwaysQuery : true,
30625 displayField : 'value',
30628 forceSelection : true,
30630 placeholder : this.dayPlaceholder,
30631 selectOnFocus : true,
30632 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30633 triggerAction : 'all',
30635 valueField : 'value',
30636 store : new Roo.data.SimpleStore({
30637 data : (function() {
30639 _this.fireEvent('days', _this, days);
30642 fields : [ 'value' ]
30645 select : function (_self, record, index)
30647 _this.setValue(_this.getValue());
30652 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30654 this.monthField = new Roo.bootstrap.MonthField({
30655 after : '<i class=\"fa fa-calendar\"></i>',
30656 allowBlank : this.monthAllowBlank,
30657 placeholder : this.monthPlaceholder,
30660 render : function (_self)
30662 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30663 e.preventDefault();
30667 select : function (_self, oldvalue, newvalue)
30669 _this.setValue(_this.getValue());
30674 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30676 this.yearField = new Roo.bootstrap.ComboBox({
30677 allowBlank : this.yearAllowBlank,
30678 alwaysQuery : true,
30679 displayField : 'value',
30682 forceSelection : true,
30684 placeholder : this.yearPlaceholder,
30685 selectOnFocus : true,
30686 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30687 triggerAction : 'all',
30689 valueField : 'value',
30690 store : new Roo.data.SimpleStore({
30691 data : (function() {
30693 _this.fireEvent('years', _this, years);
30696 fields : [ 'value' ]
30699 select : function (_self, record, index)
30701 _this.setValue(_this.getValue());
30706 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30709 setValue : function(v, format)
30711 this.inputEl.dom.value = v;
30713 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30715 var d = Date.parseDate(v, f);
30722 this.setDay(d.format(this.dayFormat));
30723 this.setMonth(d.format(this.monthFormat));
30724 this.setYear(d.format(this.yearFormat));
30731 setDay : function(v)
30733 this.dayField.setValue(v);
30734 this.inputEl.dom.value = this.getValue();
30739 setMonth : function(v)
30741 this.monthField.setValue(v, true);
30742 this.inputEl.dom.value = this.getValue();
30747 setYear : function(v)
30749 this.yearField.setValue(v);
30750 this.inputEl.dom.value = this.getValue();
30755 getDay : function()
30757 return this.dayField.getValue();
30760 getMonth : function()
30762 return this.monthField.getValue();
30765 getYear : function()
30767 return this.yearField.getValue();
30770 getValue : function()
30772 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30774 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30784 this.inputEl.dom.value = '';
30789 validate : function()
30791 var d = this.dayField.validate();
30792 var m = this.monthField.validate();
30793 var y = this.yearField.validate();
30798 (!this.dayAllowBlank && !d) ||
30799 (!this.monthAllowBlank && !m) ||
30800 (!this.yearAllowBlank && !y)
30805 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30814 this.markInvalid();
30819 markValid : function()
30822 var label = this.el.select('label', true).first();
30823 var icon = this.el.select('i.fa-star', true).first();
30829 this.fireEvent('valid', this);
30833 * Mark this field as invalid
30834 * @param {String} msg The validation message
30836 markInvalid : function(msg)
30839 var label = this.el.select('label', true).first();
30840 var icon = this.el.select('i.fa-star', true).first();
30842 if(label && !icon){
30843 this.el.select('.roo-date-split-field-label', true).createChild({
30845 cls : 'text-danger fa fa-lg fa-star',
30846 tooltip : 'This field is required',
30847 style : 'margin-right:5px;'
30851 this.fireEvent('invalid', this, msg);
30854 clearInvalid : function()
30856 var label = this.el.select('label', true).first();
30857 var icon = this.el.select('i.fa-star', true).first();
30863 this.fireEvent('valid', this);
30866 getName: function()
30876 * http://masonry.desandro.com
30878 * The idea is to render all the bricks based on vertical width...
30880 * The original code extends 'outlayer' - we might need to use that....
30886 * @class Roo.bootstrap.LayoutMasonry
30887 * @extends Roo.bootstrap.Component
30888 * Bootstrap Layout Masonry class
30891 * Create a new Element
30892 * @param {Object} config The config object
30895 Roo.bootstrap.LayoutMasonry = function(config){
30897 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30901 Roo.bootstrap.LayoutMasonry.register(this);
30907 * Fire after layout the items
30908 * @param {Roo.bootstrap.LayoutMasonry} this
30909 * @param {Roo.EventObject} e
30916 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30919 * @cfg {Boolean} isLayoutInstant = no animation?
30921 isLayoutInstant : false, // needed?
30924 * @cfg {Number} boxWidth width of the columns
30929 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30934 * @cfg {Number} padWidth padding below box..
30939 * @cfg {Number} gutter gutter width..
30944 * @cfg {Number} maxCols maximum number of columns
30950 * @cfg {Boolean} isAutoInitial defalut true
30952 isAutoInitial : true,
30957 * @cfg {Boolean} isHorizontal defalut false
30959 isHorizontal : false,
30961 currentSize : null,
30967 bricks: null, //CompositeElement
30971 _isLayoutInited : false,
30973 // isAlternative : false, // only use for vertical layout...
30976 * @cfg {Number} alternativePadWidth padding below box..
30978 alternativePadWidth : 50,
30980 selectedBrick : [],
30982 getAutoCreate : function(){
30984 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30988 cls: 'blog-masonary-wrapper ' + this.cls,
30990 cls : 'mas-boxes masonary'
30997 getChildContainer: function( )
30999 if (this.boxesEl) {
31000 return this.boxesEl;
31003 this.boxesEl = this.el.select('.mas-boxes').first();
31005 return this.boxesEl;
31009 initEvents : function()
31013 if(this.isAutoInitial){
31014 Roo.log('hook children rendered');
31015 this.on('childrenrendered', function() {
31016 Roo.log('children rendered');
31022 initial : function()
31024 this.selectedBrick = [];
31026 this.currentSize = this.el.getBox(true);
31028 Roo.EventManager.onWindowResize(this.resize, this);
31030 if(!this.isAutoInitial){
31038 //this.layout.defer(500,this);
31042 resize : function()
31044 var cs = this.el.getBox(true);
31047 this.currentSize.width == cs.width &&
31048 this.currentSize.x == cs.x &&
31049 this.currentSize.height == cs.height &&
31050 this.currentSize.y == cs.y
31052 Roo.log("no change in with or X or Y");
31056 this.currentSize = cs;
31062 layout : function()
31064 this._resetLayout();
31066 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31068 this.layoutItems( isInstant );
31070 this._isLayoutInited = true;
31072 this.fireEvent('layout', this);
31076 _resetLayout : function()
31078 if(this.isHorizontal){
31079 this.horizontalMeasureColumns();
31083 this.verticalMeasureColumns();
31087 verticalMeasureColumns : function()
31089 this.getContainerWidth();
31091 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31092 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31096 var boxWidth = this.boxWidth + this.padWidth;
31098 if(this.containerWidth < this.boxWidth){
31099 boxWidth = this.containerWidth
31102 var containerWidth = this.containerWidth;
31104 var cols = Math.floor(containerWidth / boxWidth);
31106 this.cols = Math.max( cols, 1 );
31108 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31110 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31112 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31114 this.colWidth = boxWidth + avail - this.padWidth;
31116 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31117 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31120 horizontalMeasureColumns : function()
31122 this.getContainerWidth();
31124 var boxWidth = this.boxWidth;
31126 if(this.containerWidth < boxWidth){
31127 boxWidth = this.containerWidth;
31130 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31132 this.el.setHeight(boxWidth);
31136 getContainerWidth : function()
31138 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31141 layoutItems : function( isInstant )
31143 Roo.log(this.bricks);
31145 var items = Roo.apply([], this.bricks);
31147 if(this.isHorizontal){
31148 this._horizontalLayoutItems( items , isInstant );
31152 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31153 // this._verticalAlternativeLayoutItems( items , isInstant );
31157 this._verticalLayoutItems( items , isInstant );
31161 _verticalLayoutItems : function ( items , isInstant)
31163 if ( !items || !items.length ) {
31168 ['xs', 'xs', 'xs', 'tall'],
31169 ['xs', 'xs', 'tall'],
31170 ['xs', 'xs', 'sm'],
31171 ['xs', 'xs', 'xs'],
31177 ['sm', 'xs', 'xs'],
31181 ['tall', 'xs', 'xs', 'xs'],
31182 ['tall', 'xs', 'xs'],
31194 Roo.each(items, function(item, k){
31196 switch (item.size) {
31197 // these layouts take up a full box,
31208 boxes.push([item]);
31231 var filterPattern = function(box, length)
31239 var pattern = box.slice(0, length);
31243 Roo.each(pattern, function(i){
31244 format.push(i.size);
31247 Roo.each(standard, function(s){
31249 if(String(s) != String(format)){
31258 if(!match && length == 1){
31263 filterPattern(box, length - 1);
31267 queue.push(pattern);
31269 box = box.slice(length, box.length);
31271 filterPattern(box, 4);
31277 Roo.each(boxes, function(box, k){
31283 if(box.length == 1){
31288 filterPattern(box, 4);
31292 this._processVerticalLayoutQueue( queue, isInstant );
31296 // _verticalAlternativeLayoutItems : function( items , isInstant )
31298 // if ( !items || !items.length ) {
31302 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31306 _horizontalLayoutItems : function ( items , isInstant)
31308 if ( !items || !items.length || items.length < 3) {
31314 var eItems = items.slice(0, 3);
31316 items = items.slice(3, items.length);
31319 ['xs', 'xs', 'xs', 'wide'],
31320 ['xs', 'xs', 'wide'],
31321 ['xs', 'xs', 'sm'],
31322 ['xs', 'xs', 'xs'],
31328 ['sm', 'xs', 'xs'],
31332 ['wide', 'xs', 'xs', 'xs'],
31333 ['wide', 'xs', 'xs'],
31346 Roo.each(items, function(item, k){
31348 switch (item.size) {
31359 boxes.push([item]);
31383 var filterPattern = function(box, length)
31391 var pattern = box.slice(0, length);
31395 Roo.each(pattern, function(i){
31396 format.push(i.size);
31399 Roo.each(standard, function(s){
31401 if(String(s) != String(format)){
31410 if(!match && length == 1){
31415 filterPattern(box, length - 1);
31419 queue.push(pattern);
31421 box = box.slice(length, box.length);
31423 filterPattern(box, 4);
31429 Roo.each(boxes, function(box, k){
31435 if(box.length == 1){
31440 filterPattern(box, 4);
31447 var pos = this.el.getBox(true);
31451 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31453 var hit_end = false;
31455 Roo.each(queue, function(box){
31459 Roo.each(box, function(b){
31461 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31471 Roo.each(box, function(b){
31473 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31476 mx = Math.max(mx, b.x);
31480 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31484 Roo.each(box, function(b){
31486 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31500 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31503 /** Sets position of item in DOM
31504 * @param {Element} item
31505 * @param {Number} x - horizontal position
31506 * @param {Number} y - vertical position
31507 * @param {Boolean} isInstant - disables transitions
31509 _processVerticalLayoutQueue : function( queue, isInstant )
31511 var pos = this.el.getBox(true);
31516 for (var i = 0; i < this.cols; i++){
31520 Roo.each(queue, function(box, k){
31522 var col = k % this.cols;
31524 Roo.each(box, function(b,kk){
31526 b.el.position('absolute');
31528 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31529 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31531 if(b.size == 'md-left' || b.size == 'md-right'){
31532 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31533 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31536 b.el.setWidth(width);
31537 b.el.setHeight(height);
31539 b.el.select('iframe',true).setSize(width,height);
31543 for (var i = 0; i < this.cols; i++){
31545 if(maxY[i] < maxY[col]){
31550 col = Math.min(col, i);
31554 x = pos.x + col * (this.colWidth + this.padWidth);
31558 var positions = [];
31560 switch (box.length){
31562 positions = this.getVerticalOneBoxColPositions(x, y, box);
31565 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31568 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31571 positions = this.getVerticalFourBoxColPositions(x, y, box);
31577 Roo.each(box, function(b,kk){
31579 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31581 var sz = b.el.getSize();
31583 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31591 for (var i = 0; i < this.cols; i++){
31592 mY = Math.max(mY, maxY[i]);
31595 this.el.setHeight(mY - pos.y);
31599 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31601 // var pos = this.el.getBox(true);
31604 // var maxX = pos.right;
31606 // var maxHeight = 0;
31608 // Roo.each(items, function(item, k){
31612 // item.el.position('absolute');
31614 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31616 // item.el.setWidth(width);
31618 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31620 // item.el.setHeight(height);
31623 // item.el.setXY([x, y], isInstant ? false : true);
31625 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31628 // y = y + height + this.alternativePadWidth;
31630 // maxHeight = maxHeight + height + this.alternativePadWidth;
31634 // this.el.setHeight(maxHeight);
31638 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31640 var pos = this.el.getBox(true);
31645 var maxX = pos.right;
31647 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31649 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31651 Roo.each(queue, function(box, k){
31653 Roo.each(box, function(b, kk){
31655 b.el.position('absolute');
31657 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31658 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31660 if(b.size == 'md-left' || b.size == 'md-right'){
31661 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31662 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31665 b.el.setWidth(width);
31666 b.el.setHeight(height);
31674 var positions = [];
31676 switch (box.length){
31678 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31681 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31684 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31687 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31693 Roo.each(box, function(b,kk){
31695 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31697 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31705 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31707 Roo.each(eItems, function(b,k){
31709 b.size = (k == 0) ? 'sm' : 'xs';
31710 b.x = (k == 0) ? 2 : 1;
31711 b.y = (k == 0) ? 2 : 1;
31713 b.el.position('absolute');
31715 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31717 b.el.setWidth(width);
31719 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31721 b.el.setHeight(height);
31725 var positions = [];
31728 x : maxX - this.unitWidth * 2 - this.gutter,
31733 x : maxX - this.unitWidth,
31734 y : minY + (this.unitWidth + this.gutter) * 2
31738 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31742 Roo.each(eItems, function(b,k){
31744 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31750 getVerticalOneBoxColPositions : function(x, y, box)
31754 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31756 if(box[0].size == 'md-left'){
31760 if(box[0].size == 'md-right'){
31765 x : x + (this.unitWidth + this.gutter) * rand,
31772 getVerticalTwoBoxColPositions : function(x, y, box)
31776 if(box[0].size == 'xs'){
31780 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31784 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31798 x : x + (this.unitWidth + this.gutter) * 2,
31799 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31806 getVerticalThreeBoxColPositions : function(x, y, box)
31810 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31818 x : x + (this.unitWidth + this.gutter) * 1,
31823 x : x + (this.unitWidth + this.gutter) * 2,
31831 if(box[0].size == 'xs' && box[1].size == 'xs'){
31840 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31844 x : x + (this.unitWidth + this.gutter) * 1,
31858 x : x + (this.unitWidth + this.gutter) * 2,
31863 x : x + (this.unitWidth + this.gutter) * 2,
31864 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31871 getVerticalFourBoxColPositions : function(x, y, box)
31875 if(box[0].size == 'xs'){
31884 y : y + (this.unitHeight + this.gutter) * 1
31889 y : y + (this.unitHeight + this.gutter) * 2
31893 x : x + (this.unitWidth + this.gutter) * 1,
31907 x : x + (this.unitWidth + this.gutter) * 2,
31912 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31913 y : y + (this.unitHeight + this.gutter) * 1
31917 x : x + (this.unitWidth + this.gutter) * 2,
31918 y : y + (this.unitWidth + this.gutter) * 2
31925 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31929 if(box[0].size == 'md-left'){
31931 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31938 if(box[0].size == 'md-right'){
31940 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31941 y : minY + (this.unitWidth + this.gutter) * 1
31947 var rand = Math.floor(Math.random() * (4 - box[0].y));
31950 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31951 y : minY + (this.unitWidth + this.gutter) * rand
31958 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31962 if(box[0].size == 'xs'){
31965 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31970 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31971 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31984 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31985 y : minY + (this.unitWidth + this.gutter) * 2
31992 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31996 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31999 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32004 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32005 y : minY + (this.unitWidth + this.gutter) * 1
32009 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32010 y : minY + (this.unitWidth + this.gutter) * 2
32017 if(box[0].size == 'xs' && box[1].size == 'xs'){
32020 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32025 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32030 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32031 y : minY + (this.unitWidth + this.gutter) * 1
32039 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32044 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32045 y : minY + (this.unitWidth + this.gutter) * 2
32049 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32050 y : minY + (this.unitWidth + this.gutter) * 2
32057 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32061 if(box[0].size == 'xs'){
32064 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32069 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32074 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),
32079 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32080 y : minY + (this.unitWidth + this.gutter) * 1
32088 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32093 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32094 y : minY + (this.unitWidth + this.gutter) * 2
32098 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32099 y : minY + (this.unitWidth + this.gutter) * 2
32103 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),
32104 y : minY + (this.unitWidth + this.gutter) * 2
32112 * remove a Masonry Brick
32113 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32115 removeBrick : function(brick_id)
32121 for (var i = 0; i<this.bricks.length; i++) {
32122 if (this.bricks[i].id == brick_id) {
32123 this.bricks.splice(i,1);
32124 this.el.dom.removeChild(Roo.get(brick_id).dom);
32131 * adds a Masonry Brick
32132 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32134 addBrick : function(cfg)
32136 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32137 //this.register(cn);
32138 cn.parentId = this.id;
32139 cn.render(this.el);
32144 * register a Masonry Brick
32145 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32148 register : function(brick)
32150 this.bricks.push(brick);
32151 brick.masonryId = this.id;
32155 * clear all the Masonry Brick
32157 clearAll : function()
32160 //this.getChildContainer().dom.innerHTML = "";
32161 this.el.dom.innerHTML = '';
32164 getSelected : function()
32166 if (!this.selectedBrick) {
32170 return this.selectedBrick;
32174 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32178 * register a Masonry Layout
32179 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32182 register : function(layout)
32184 this.groups[layout.id] = layout;
32187 * fetch a Masonry Layout based on the masonry layout ID
32188 * @param {string} the masonry layout to add
32189 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32192 get: function(layout_id) {
32193 if (typeof(this.groups[layout_id]) == 'undefined') {
32196 return this.groups[layout_id] ;
32208 * http://masonry.desandro.com
32210 * The idea is to render all the bricks based on vertical width...
32212 * The original code extends 'outlayer' - we might need to use that....
32218 * @class Roo.bootstrap.LayoutMasonryAuto
32219 * @extends Roo.bootstrap.Component
32220 * Bootstrap Layout Masonry class
32223 * Create a new Element
32224 * @param {Object} config The config object
32227 Roo.bootstrap.LayoutMasonryAuto = function(config){
32228 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32231 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32234 * @cfg {Boolean} isFitWidth - resize the width..
32236 isFitWidth : false, // options..
32238 * @cfg {Boolean} isOriginLeft = left align?
32240 isOriginLeft : true,
32242 * @cfg {Boolean} isOriginTop = top align?
32244 isOriginTop : false,
32246 * @cfg {Boolean} isLayoutInstant = no animation?
32248 isLayoutInstant : false, // needed?
32250 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32252 isResizingContainer : true,
32254 * @cfg {Number} columnWidth width of the columns
32260 * @cfg {Number} maxCols maximum number of columns
32265 * @cfg {Number} padHeight padding below box..
32271 * @cfg {Boolean} isAutoInitial defalut true
32274 isAutoInitial : true,
32280 initialColumnWidth : 0,
32281 currentSize : null,
32283 colYs : null, // array.
32290 bricks: null, //CompositeElement
32291 cols : 0, // array?
32292 // element : null, // wrapped now this.el
32293 _isLayoutInited : null,
32296 getAutoCreate : function(){
32300 cls: 'blog-masonary-wrapper ' + this.cls,
32302 cls : 'mas-boxes masonary'
32309 getChildContainer: function( )
32311 if (this.boxesEl) {
32312 return this.boxesEl;
32315 this.boxesEl = this.el.select('.mas-boxes').first();
32317 return this.boxesEl;
32321 initEvents : function()
32325 if(this.isAutoInitial){
32326 Roo.log('hook children rendered');
32327 this.on('childrenrendered', function() {
32328 Roo.log('children rendered');
32335 initial : function()
32337 this.reloadItems();
32339 this.currentSize = this.el.getBox(true);
32341 /// was window resize... - let's see if this works..
32342 Roo.EventManager.onWindowResize(this.resize, this);
32344 if(!this.isAutoInitial){
32349 this.layout.defer(500,this);
32352 reloadItems: function()
32354 this.bricks = this.el.select('.masonry-brick', true);
32356 this.bricks.each(function(b) {
32357 //Roo.log(b.getSize());
32358 if (!b.attr('originalwidth')) {
32359 b.attr('originalwidth', b.getSize().width);
32364 Roo.log(this.bricks.elements.length);
32367 resize : function()
32370 var cs = this.el.getBox(true);
32372 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32373 Roo.log("no change in with or X");
32376 this.currentSize = cs;
32380 layout : function()
32383 this._resetLayout();
32384 //this._manageStamps();
32386 // don't animate first layout
32387 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32388 this.layoutItems( isInstant );
32390 // flag for initalized
32391 this._isLayoutInited = true;
32394 layoutItems : function( isInstant )
32396 //var items = this._getItemsForLayout( this.items );
32397 // original code supports filtering layout items.. we just ignore it..
32399 this._layoutItems( this.bricks , isInstant );
32401 this._postLayout();
32403 _layoutItems : function ( items , isInstant)
32405 //this.fireEvent( 'layout', this, items );
32408 if ( !items || !items.elements.length ) {
32409 // no items, emit event with empty array
32414 items.each(function(item) {
32415 Roo.log("layout item");
32417 // get x/y object from method
32418 var position = this._getItemLayoutPosition( item );
32420 position.item = item;
32421 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32422 queue.push( position );
32425 this._processLayoutQueue( queue );
32427 /** Sets position of item in DOM
32428 * @param {Element} item
32429 * @param {Number} x - horizontal position
32430 * @param {Number} y - vertical position
32431 * @param {Boolean} isInstant - disables transitions
32433 _processLayoutQueue : function( queue )
32435 for ( var i=0, len = queue.length; i < len; i++ ) {
32436 var obj = queue[i];
32437 obj.item.position('absolute');
32438 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32444 * Any logic you want to do after each layout,
32445 * i.e. size the container
32447 _postLayout : function()
32449 this.resizeContainer();
32452 resizeContainer : function()
32454 if ( !this.isResizingContainer ) {
32457 var size = this._getContainerSize();
32459 this.el.setSize(size.width,size.height);
32460 this.boxesEl.setSize(size.width,size.height);
32466 _resetLayout : function()
32468 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32469 this.colWidth = this.el.getWidth();
32470 //this.gutter = this.el.getWidth();
32472 this.measureColumns();
32478 this.colYs.push( 0 );
32484 measureColumns : function()
32486 this.getContainerWidth();
32487 // if columnWidth is 0, default to outerWidth of first item
32488 if ( !this.columnWidth ) {
32489 var firstItem = this.bricks.first();
32490 Roo.log(firstItem);
32491 this.columnWidth = this.containerWidth;
32492 if (firstItem && firstItem.attr('originalwidth') ) {
32493 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32495 // columnWidth fall back to item of first element
32496 Roo.log("set column width?");
32497 this.initialColumnWidth = this.columnWidth ;
32499 // if first elem has no width, default to size of container
32504 if (this.initialColumnWidth) {
32505 this.columnWidth = this.initialColumnWidth;
32510 // column width is fixed at the top - however if container width get's smaller we should
32513 // this bit calcs how man columns..
32515 var columnWidth = this.columnWidth += this.gutter;
32517 // calculate columns
32518 var containerWidth = this.containerWidth + this.gutter;
32520 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32521 // fix rounding errors, typically with gutters
32522 var excess = columnWidth - containerWidth % columnWidth;
32525 // if overshoot is less than a pixel, round up, otherwise floor it
32526 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32527 cols = Math[ mathMethod ]( cols );
32528 this.cols = Math.max( cols, 1 );
32529 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32531 // padding positioning..
32532 var totalColWidth = this.cols * this.columnWidth;
32533 var padavail = this.containerWidth - totalColWidth;
32534 // so for 2 columns - we need 3 'pads'
32536 var padNeeded = (1+this.cols) * this.padWidth;
32538 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32540 this.columnWidth += padExtra
32541 //this.padWidth = Math.floor(padavail / ( this.cols));
32543 // adjust colum width so that padding is fixed??
32545 // we have 3 columns ... total = width * 3
32546 // we have X left over... that should be used by
32548 //if (this.expandC) {
32556 getContainerWidth : function()
32558 /* // container is parent if fit width
32559 var container = this.isFitWidth ? this.element.parentNode : this.element;
32560 // check that this.size and size are there
32561 // IE8 triggers resize on body size change, so they might not be
32563 var size = getSize( container ); //FIXME
32564 this.containerWidth = size && size.innerWidth; //FIXME
32567 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32571 _getItemLayoutPosition : function( item ) // what is item?
32573 // we resize the item to our columnWidth..
32575 item.setWidth(this.columnWidth);
32576 item.autoBoxAdjust = false;
32578 var sz = item.getSize();
32580 // how many columns does this brick span
32581 var remainder = this.containerWidth % this.columnWidth;
32583 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32584 // round if off by 1 pixel, otherwise use ceil
32585 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32586 colSpan = Math.min( colSpan, this.cols );
32588 // normally this should be '1' as we dont' currently allow multi width columns..
32590 var colGroup = this._getColGroup( colSpan );
32591 // get the minimum Y value from the columns
32592 var minimumY = Math.min.apply( Math, colGroup );
32593 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32595 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32597 // position the brick
32599 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32600 y: this.currentSize.y + minimumY + this.padHeight
32604 // apply setHeight to necessary columns
32605 var setHeight = minimumY + sz.height + this.padHeight;
32606 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32608 var setSpan = this.cols + 1 - colGroup.length;
32609 for ( var i = 0; i < setSpan; i++ ) {
32610 this.colYs[ shortColIndex + i ] = setHeight ;
32617 * @param {Number} colSpan - number of columns the element spans
32618 * @returns {Array} colGroup
32620 _getColGroup : function( colSpan )
32622 if ( colSpan < 2 ) {
32623 // if brick spans only one column, use all the column Ys
32628 // how many different places could this brick fit horizontally
32629 var groupCount = this.cols + 1 - colSpan;
32630 // for each group potential horizontal position
32631 for ( var i = 0; i < groupCount; i++ ) {
32632 // make an array of colY values for that one group
32633 var groupColYs = this.colYs.slice( i, i + colSpan );
32634 // and get the max value of the array
32635 colGroup[i] = Math.max.apply( Math, groupColYs );
32640 _manageStamp : function( stamp )
32642 var stampSize = stamp.getSize();
32643 var offset = stamp.getBox();
32644 // get the columns that this stamp affects
32645 var firstX = this.isOriginLeft ? offset.x : offset.right;
32646 var lastX = firstX + stampSize.width;
32647 var firstCol = Math.floor( firstX / this.columnWidth );
32648 firstCol = Math.max( 0, firstCol );
32650 var lastCol = Math.floor( lastX / this.columnWidth );
32651 // lastCol should not go over if multiple of columnWidth #425
32652 lastCol -= lastX % this.columnWidth ? 0 : 1;
32653 lastCol = Math.min( this.cols - 1, lastCol );
32655 // set colYs to bottom of the stamp
32656 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32659 for ( var i = firstCol; i <= lastCol; i++ ) {
32660 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32665 _getContainerSize : function()
32667 this.maxY = Math.max.apply( Math, this.colYs );
32672 if ( this.isFitWidth ) {
32673 size.width = this._getContainerFitWidth();
32679 _getContainerFitWidth : function()
32681 var unusedCols = 0;
32682 // count unused columns
32685 if ( this.colYs[i] !== 0 ) {
32690 // fit container to columns that have been used
32691 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32694 needsResizeLayout : function()
32696 var previousWidth = this.containerWidth;
32697 this.getContainerWidth();
32698 return previousWidth !== this.containerWidth;
32713 * @class Roo.bootstrap.MasonryBrick
32714 * @extends Roo.bootstrap.Component
32715 * Bootstrap MasonryBrick class
32718 * Create a new MasonryBrick
32719 * @param {Object} config The config object
32722 Roo.bootstrap.MasonryBrick = function(config){
32724 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32726 Roo.bootstrap.MasonryBrick.register(this);
32732 * When a MasonryBrick is clcik
32733 * @param {Roo.bootstrap.MasonryBrick} this
32734 * @param {Roo.EventObject} e
32740 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32743 * @cfg {String} title
32747 * @cfg {String} html
32751 * @cfg {String} bgimage
32755 * @cfg {String} videourl
32759 * @cfg {String} cls
32763 * @cfg {String} href
32767 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32772 * @cfg {String} placetitle (center|bottom)
32777 * @cfg {Boolean} isFitContainer defalut true
32779 isFitContainer : true,
32782 * @cfg {Boolean} preventDefault defalut false
32784 preventDefault : false,
32787 * @cfg {Boolean} inverse defalut false
32789 maskInverse : false,
32791 getAutoCreate : function()
32793 if(!this.isFitContainer){
32794 return this.getSplitAutoCreate();
32797 var cls = 'masonry-brick masonry-brick-full';
32799 if(this.href.length){
32800 cls += ' masonry-brick-link';
32803 if(this.bgimage.length){
32804 cls += ' masonry-brick-image';
32807 if(this.maskInverse){
32808 cls += ' mask-inverse';
32811 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32812 cls += ' enable-mask';
32816 cls += ' masonry-' + this.size + '-brick';
32819 if(this.placetitle.length){
32821 switch (this.placetitle) {
32823 cls += ' masonry-center-title';
32826 cls += ' masonry-bottom-title';
32833 if(!this.html.length && !this.bgimage.length){
32834 cls += ' masonry-center-title';
32837 if(!this.html.length && this.bgimage.length){
32838 cls += ' masonry-bottom-title';
32843 cls += ' ' + this.cls;
32847 tag: (this.href.length) ? 'a' : 'div',
32852 cls: 'masonry-brick-mask'
32856 cls: 'masonry-brick-paragraph',
32862 if(this.href.length){
32863 cfg.href = this.href;
32866 var cn = cfg.cn[1].cn;
32868 if(this.title.length){
32871 cls: 'masonry-brick-title',
32876 if(this.html.length){
32879 cls: 'masonry-brick-text',
32884 if (!this.title.length && !this.html.length) {
32885 cfg.cn[1].cls += ' hide';
32888 if(this.bgimage.length){
32891 cls: 'masonry-brick-image-view',
32896 if(this.videourl.length){
32897 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32898 // youtube support only?
32901 cls: 'masonry-brick-image-view',
32904 allowfullscreen : true
32912 getSplitAutoCreate : function()
32914 var cls = 'masonry-brick masonry-brick-split';
32916 if(this.href.length){
32917 cls += ' masonry-brick-link';
32920 if(this.bgimage.length){
32921 cls += ' masonry-brick-image';
32925 cls += ' masonry-' + this.size + '-brick';
32928 switch (this.placetitle) {
32930 cls += ' masonry-center-title';
32933 cls += ' masonry-bottom-title';
32936 if(!this.bgimage.length){
32937 cls += ' masonry-center-title';
32940 if(this.bgimage.length){
32941 cls += ' masonry-bottom-title';
32947 cls += ' ' + this.cls;
32951 tag: (this.href.length) ? 'a' : 'div',
32956 cls: 'masonry-brick-split-head',
32960 cls: 'masonry-brick-paragraph',
32967 cls: 'masonry-brick-split-body',
32973 if(this.href.length){
32974 cfg.href = this.href;
32977 if(this.title.length){
32978 cfg.cn[0].cn[0].cn.push({
32980 cls: 'masonry-brick-title',
32985 if(this.html.length){
32986 cfg.cn[1].cn.push({
32988 cls: 'masonry-brick-text',
32993 if(this.bgimage.length){
32994 cfg.cn[0].cn.push({
32996 cls: 'masonry-brick-image-view',
33001 if(this.videourl.length){
33002 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33003 // youtube support only?
33004 cfg.cn[0].cn.cn.push({
33006 cls: 'masonry-brick-image-view',
33009 allowfullscreen : true
33016 initEvents: function()
33018 switch (this.size) {
33051 this.el.on('touchstart', this.onTouchStart, this);
33052 this.el.on('touchmove', this.onTouchMove, this);
33053 this.el.on('touchend', this.onTouchEnd, this);
33054 this.el.on('contextmenu', this.onContextMenu, this);
33056 this.el.on('mouseenter' ,this.enter, this);
33057 this.el.on('mouseleave', this.leave, this);
33058 this.el.on('click', this.onClick, this);
33061 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33062 this.parent().bricks.push(this);
33067 onClick: function(e, el)
33069 var time = this.endTimer - this.startTimer;
33070 // Roo.log(e.preventDefault());
33073 e.preventDefault();
33078 if(!this.preventDefault){
33082 e.preventDefault();
33084 if (this.activeClass != '') {
33085 this.selectBrick();
33088 this.fireEvent('click', this, e);
33091 enter: function(e, el)
33093 e.preventDefault();
33095 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33099 if(this.bgimage.length && this.html.length){
33100 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33104 leave: function(e, el)
33106 e.preventDefault();
33108 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33112 if(this.bgimage.length && this.html.length){
33113 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33117 onTouchStart: function(e, el)
33119 // e.preventDefault();
33121 this.touchmoved = false;
33123 if(!this.isFitContainer){
33127 if(!this.bgimage.length || !this.html.length){
33131 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33133 this.timer = new Date().getTime();
33137 onTouchMove: function(e, el)
33139 this.touchmoved = true;
33142 onContextMenu : function(e,el)
33144 e.preventDefault();
33145 e.stopPropagation();
33149 onTouchEnd: function(e, el)
33151 // e.preventDefault();
33153 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33160 if(!this.bgimage.length || !this.html.length){
33162 if(this.href.length){
33163 window.location.href = this.href;
33169 if(!this.isFitContainer){
33173 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33175 window.location.href = this.href;
33178 //selection on single brick only
33179 selectBrick : function() {
33181 if (!this.parentId) {
33185 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33186 var index = m.selectedBrick.indexOf(this.id);
33189 m.selectedBrick.splice(index,1);
33190 this.el.removeClass(this.activeClass);
33194 for(var i = 0; i < m.selectedBrick.length; i++) {
33195 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33196 b.el.removeClass(b.activeClass);
33199 m.selectedBrick = [];
33201 m.selectedBrick.push(this.id);
33202 this.el.addClass(this.activeClass);
33206 isSelected : function(){
33207 return this.el.hasClass(this.activeClass);
33212 Roo.apply(Roo.bootstrap.MasonryBrick, {
33215 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33217 * register a Masonry Brick
33218 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33221 register : function(brick)
33223 //this.groups[brick.id] = brick;
33224 this.groups.add(brick.id, brick);
33227 * fetch a masonry brick based on the masonry brick ID
33228 * @param {string} the masonry brick to add
33229 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33232 get: function(brick_id)
33234 // if (typeof(this.groups[brick_id]) == 'undefined') {
33237 // return this.groups[brick_id] ;
33239 if(this.groups.key(brick_id)) {
33240 return this.groups.key(brick_id);
33258 * @class Roo.bootstrap.Brick
33259 * @extends Roo.bootstrap.Component
33260 * Bootstrap Brick class
33263 * Create a new Brick
33264 * @param {Object} config The config object
33267 Roo.bootstrap.Brick = function(config){
33268 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33274 * When a Brick is click
33275 * @param {Roo.bootstrap.Brick} this
33276 * @param {Roo.EventObject} e
33282 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33285 * @cfg {String} title
33289 * @cfg {String} html
33293 * @cfg {String} bgimage
33297 * @cfg {String} cls
33301 * @cfg {String} href
33305 * @cfg {String} video
33309 * @cfg {Boolean} square
33313 getAutoCreate : function()
33315 var cls = 'roo-brick';
33317 if(this.href.length){
33318 cls += ' roo-brick-link';
33321 if(this.bgimage.length){
33322 cls += ' roo-brick-image';
33325 if(!this.html.length && !this.bgimage.length){
33326 cls += ' roo-brick-center-title';
33329 if(!this.html.length && this.bgimage.length){
33330 cls += ' roo-brick-bottom-title';
33334 cls += ' ' + this.cls;
33338 tag: (this.href.length) ? 'a' : 'div',
33343 cls: 'roo-brick-paragraph',
33349 if(this.href.length){
33350 cfg.href = this.href;
33353 var cn = cfg.cn[0].cn;
33355 if(this.title.length){
33358 cls: 'roo-brick-title',
33363 if(this.html.length){
33366 cls: 'roo-brick-text',
33373 if(this.bgimage.length){
33376 cls: 'roo-brick-image-view',
33384 initEvents: function()
33386 if(this.title.length || this.html.length){
33387 this.el.on('mouseenter' ,this.enter, this);
33388 this.el.on('mouseleave', this.leave, this);
33391 Roo.EventManager.onWindowResize(this.resize, this);
33393 if(this.bgimage.length){
33394 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33395 this.imageEl.on('load', this.onImageLoad, this);
33402 onImageLoad : function()
33407 resize : function()
33409 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33411 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33413 if(this.bgimage.length){
33414 var image = this.el.select('.roo-brick-image-view', true).first();
33416 image.setWidth(paragraph.getWidth());
33419 image.setHeight(paragraph.getWidth());
33422 this.el.setHeight(image.getHeight());
33423 paragraph.setHeight(image.getHeight());
33429 enter: function(e, el)
33431 e.preventDefault();
33433 if(this.bgimage.length){
33434 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33435 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33439 leave: function(e, el)
33441 e.preventDefault();
33443 if(this.bgimage.length){
33444 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33445 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33460 * @class Roo.bootstrap.NumberField
33461 * @extends Roo.bootstrap.Input
33462 * Bootstrap NumberField class
33468 * Create a new NumberField
33469 * @param {Object} config The config object
33472 Roo.bootstrap.NumberField = function(config){
33473 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33476 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33479 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33481 allowDecimals : true,
33483 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33485 decimalSeparator : ".",
33487 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33489 decimalPrecision : 2,
33491 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33493 allowNegative : true,
33496 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33500 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33502 minValue : Number.NEGATIVE_INFINITY,
33504 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33506 maxValue : Number.MAX_VALUE,
33508 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33510 minText : "The minimum value for this field is {0}",
33512 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33514 maxText : "The maximum value for this field is {0}",
33516 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33517 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33519 nanText : "{0} is not a valid number",
33521 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33523 thousandsDelimiter : false,
33525 * @cfg {String} valueAlign alignment of value
33527 valueAlign : "left",
33529 getAutoCreate : function()
33531 var hiddenInput = {
33535 cls: 'hidden-number-input'
33539 hiddenInput.name = this.name;
33544 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33546 this.name = hiddenInput.name;
33548 if(cfg.cn.length > 0) {
33549 cfg.cn.push(hiddenInput);
33556 initEvents : function()
33558 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33560 var allowed = "0123456789";
33562 if(this.allowDecimals){
33563 allowed += this.decimalSeparator;
33566 if(this.allowNegative){
33570 if(this.thousandsDelimiter) {
33574 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33576 var keyPress = function(e){
33578 var k = e.getKey();
33580 var c = e.getCharCode();
33583 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33584 allowed.indexOf(String.fromCharCode(c)) === -1
33590 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33594 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33599 this.el.on("keypress", keyPress, this);
33602 validateValue : function(value)
33605 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33609 var num = this.parseValue(value);
33612 this.markInvalid(String.format(this.nanText, value));
33616 if(num < this.minValue){
33617 this.markInvalid(String.format(this.minText, this.minValue));
33621 if(num > this.maxValue){
33622 this.markInvalid(String.format(this.maxText, this.maxValue));
33629 getValue : function()
33631 var v = this.hiddenEl().getValue();
33633 return this.fixPrecision(this.parseValue(v));
33636 parseValue : function(value)
33638 if(this.thousandsDelimiter) {
33640 r = new RegExp(",", "g");
33641 value = value.replace(r, "");
33644 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33645 return isNaN(value) ? '' : value;
33648 fixPrecision : function(value)
33650 if(this.thousandsDelimiter) {
33652 r = new RegExp(",", "g");
33653 value = value.replace(r, "");
33656 var nan = isNaN(value);
33658 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33659 return nan ? '' : value;
33661 return parseFloat(value).toFixed(this.decimalPrecision);
33664 setValue : function(v)
33666 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33672 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33674 this.inputEl().dom.value = (v == '') ? '' :
33675 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33677 if(!this.allowZero && v === '0') {
33678 this.hiddenEl().dom.value = '';
33679 this.inputEl().dom.value = '';
33686 decimalPrecisionFcn : function(v)
33688 return Math.floor(v);
33691 beforeBlur : function()
33693 var v = this.parseValue(this.getRawValue());
33695 if(v || v === 0 || v === ''){
33700 hiddenEl : function()
33702 return this.el.select('input.hidden-number-input',true).first();
33714 * @class Roo.bootstrap.DocumentSlider
33715 * @extends Roo.bootstrap.Component
33716 * Bootstrap DocumentSlider class
33719 * Create a new DocumentViewer
33720 * @param {Object} config The config object
33723 Roo.bootstrap.DocumentSlider = function(config){
33724 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33731 * Fire after initEvent
33732 * @param {Roo.bootstrap.DocumentSlider} this
33737 * Fire after update
33738 * @param {Roo.bootstrap.DocumentSlider} this
33744 * @param {Roo.bootstrap.DocumentSlider} this
33750 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33756 getAutoCreate : function()
33760 cls : 'roo-document-slider',
33764 cls : 'roo-document-slider-header',
33768 cls : 'roo-document-slider-header-title'
33774 cls : 'roo-document-slider-body',
33778 cls : 'roo-document-slider-prev',
33782 cls : 'fa fa-chevron-left'
33788 cls : 'roo-document-slider-thumb',
33792 cls : 'roo-document-slider-image'
33798 cls : 'roo-document-slider-next',
33802 cls : 'fa fa-chevron-right'
33814 initEvents : function()
33816 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33817 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33819 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33820 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33822 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33823 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33825 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33826 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33828 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33829 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33831 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33832 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33834 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33835 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33837 this.thumbEl.on('click', this.onClick, this);
33839 this.prevIndicator.on('click', this.prev, this);
33841 this.nextIndicator.on('click', this.next, this);
33845 initial : function()
33847 if(this.files.length){
33848 this.indicator = 1;
33852 this.fireEvent('initial', this);
33855 update : function()
33857 this.imageEl.attr('src', this.files[this.indicator - 1]);
33859 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33861 this.prevIndicator.show();
33863 if(this.indicator == 1){
33864 this.prevIndicator.hide();
33867 this.nextIndicator.show();
33869 if(this.indicator == this.files.length){
33870 this.nextIndicator.hide();
33873 this.thumbEl.scrollTo('top');
33875 this.fireEvent('update', this);
33878 onClick : function(e)
33880 e.preventDefault();
33882 this.fireEvent('click', this);
33887 e.preventDefault();
33889 this.indicator = Math.max(1, this.indicator - 1);
33896 e.preventDefault();
33898 this.indicator = Math.min(this.files.length, this.indicator + 1);
33912 * @class Roo.bootstrap.RadioSet
33913 * @extends Roo.bootstrap.Input
33914 * Bootstrap RadioSet class
33915 * @cfg {String} indicatorpos (left|right) default left
33916 * @cfg {Boolean} inline (true|false) inline the element (default true)
33917 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33919 * Create a new RadioSet
33920 * @param {Object} config The config object
33923 Roo.bootstrap.RadioSet = function(config){
33925 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33929 Roo.bootstrap.RadioSet.register(this);
33934 * Fires when the element is checked or unchecked.
33935 * @param {Roo.bootstrap.RadioSet} this This radio
33936 * @param {Roo.bootstrap.Radio} item The checked item
33941 * Fires when the element is click.
33942 * @param {Roo.bootstrap.RadioSet} this This radio set
33943 * @param {Roo.bootstrap.Radio} item The checked item
33944 * @param {Roo.EventObject} e The event object
33951 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33959 indicatorpos : 'left',
33961 getAutoCreate : function()
33965 cls : 'roo-radio-set-label',
33969 html : this.fieldLabel
33974 if(this.indicatorpos == 'left'){
33977 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33978 tooltip : 'This field is required'
33983 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33984 tooltip : 'This field is required'
33990 cls : 'roo-radio-set-items'
33993 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33995 if (align === 'left' && this.fieldLabel.length) {
33998 cls : "roo-radio-set-right",
34004 if(this.labelWidth > 12){
34005 label.style = "width: " + this.labelWidth + 'px';
34008 if(this.labelWidth < 13 && this.labelmd == 0){
34009 this.labelmd = this.labelWidth;
34012 if(this.labellg > 0){
34013 label.cls += ' col-lg-' + this.labellg;
34014 items.cls += ' col-lg-' + (12 - this.labellg);
34017 if(this.labelmd > 0){
34018 label.cls += ' col-md-' + this.labelmd;
34019 items.cls += ' col-md-' + (12 - this.labelmd);
34022 if(this.labelsm > 0){
34023 label.cls += ' col-sm-' + this.labelsm;
34024 items.cls += ' col-sm-' + (12 - this.labelsm);
34027 if(this.labelxs > 0){
34028 label.cls += ' col-xs-' + this.labelxs;
34029 items.cls += ' col-xs-' + (12 - this.labelxs);
34035 cls : 'roo-radio-set',
34039 cls : 'roo-radio-set-input',
34042 value : this.value ? this.value : ''
34049 if(this.weight.length){
34050 cfg.cls += ' roo-radio-' + this.weight;
34054 cfg.cls += ' roo-radio-set-inline';
34058 ['xs','sm','md','lg'].map(function(size){
34059 if (settings[size]) {
34060 cfg.cls += ' col-' + size + '-' + settings[size];
34068 initEvents : function()
34070 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34071 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34073 if(!this.fieldLabel.length){
34074 this.labelEl.hide();
34077 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34078 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34080 this.indicator = this.indicatorEl();
34082 if(this.indicator){
34083 this.indicator.addClass('invisible');
34086 this.originalValue = this.getValue();
34090 inputEl: function ()
34092 return this.el.select('.roo-radio-set-input', true).first();
34095 getChildContainer : function()
34097 return this.itemsEl;
34100 register : function(item)
34102 this.radioes.push(item);
34106 validate : function()
34108 if(this.getVisibilityEl().hasClass('hidden')){
34114 Roo.each(this.radioes, function(i){
34123 if(this.allowBlank) {
34127 if(this.disabled || valid){
34132 this.markInvalid();
34137 markValid : function()
34139 if(this.labelEl.isVisible(true)){
34140 this.indicatorEl().removeClass('visible');
34141 this.indicatorEl().addClass('invisible');
34144 this.el.removeClass([this.invalidClass, this.validClass]);
34145 this.el.addClass(this.validClass);
34147 this.fireEvent('valid', this);
34150 markInvalid : function(msg)
34152 if(this.allowBlank || this.disabled){
34156 if(this.labelEl.isVisible(true)){
34157 this.indicatorEl().removeClass('invisible');
34158 this.indicatorEl().addClass('visible');
34161 this.el.removeClass([this.invalidClass, this.validClass]);
34162 this.el.addClass(this.invalidClass);
34164 this.fireEvent('invalid', this, msg);
34168 setValue : function(v, suppressEvent)
34170 if(this.value === v){
34177 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34180 Roo.each(this.radioes, function(i){
34182 i.el.removeClass('checked');
34185 Roo.each(this.radioes, function(i){
34187 if(i.value === v || i.value.toString() === v.toString()){
34189 i.el.addClass('checked');
34191 if(suppressEvent !== true){
34192 this.fireEvent('check', this, i);
34203 clearInvalid : function(){
34205 if(!this.el || this.preventMark){
34209 this.el.removeClass([this.invalidClass]);
34211 this.fireEvent('valid', this);
34216 Roo.apply(Roo.bootstrap.RadioSet, {
34220 register : function(set)
34222 this.groups[set.name] = set;
34225 get: function(name)
34227 if (typeof(this.groups[name]) == 'undefined') {
34231 return this.groups[name] ;
34237 * Ext JS Library 1.1.1
34238 * Copyright(c) 2006-2007, Ext JS, LLC.
34240 * Originally Released Under LGPL - original licence link has changed is not relivant.
34243 * <script type="text/javascript">
34248 * @class Roo.bootstrap.SplitBar
34249 * @extends Roo.util.Observable
34250 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34254 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34255 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34256 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34257 split.minSize = 100;
34258 split.maxSize = 600;
34259 split.animate = true;
34260 split.on('moved', splitterMoved);
34263 * Create a new SplitBar
34264 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34265 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34266 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34267 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34268 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34269 position of the SplitBar).
34271 Roo.bootstrap.SplitBar = function(cfg){
34276 // dragElement : elm
34277 // resizingElement: el,
34279 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34280 // placement : Roo.bootstrap.SplitBar.LEFT ,
34281 // existingProxy ???
34284 this.el = Roo.get(cfg.dragElement, true);
34285 this.el.dom.unselectable = "on";
34287 this.resizingEl = Roo.get(cfg.resizingElement, true);
34291 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34292 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34295 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34298 * The minimum size of the resizing element. (Defaults to 0)
34304 * The maximum size of the resizing element. (Defaults to 2000)
34307 this.maxSize = 2000;
34310 * Whether to animate the transition to the new size
34313 this.animate = false;
34316 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34319 this.useShim = false;
34324 if(!cfg.existingProxy){
34326 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34328 this.proxy = Roo.get(cfg.existingProxy).dom;
34331 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34334 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34337 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34340 this.dragSpecs = {};
34343 * @private The adapter to use to positon and resize elements
34345 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34346 this.adapter.init(this);
34348 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34350 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34351 this.el.addClass("roo-splitbar-h");
34354 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34355 this.el.addClass("roo-splitbar-v");
34361 * Fires when the splitter is moved (alias for {@link #event-moved})
34362 * @param {Roo.bootstrap.SplitBar} this
34363 * @param {Number} newSize the new width or height
34368 * Fires when the splitter is moved
34369 * @param {Roo.bootstrap.SplitBar} this
34370 * @param {Number} newSize the new width or height
34374 * @event beforeresize
34375 * Fires before the splitter is dragged
34376 * @param {Roo.bootstrap.SplitBar} this
34378 "beforeresize" : true,
34380 "beforeapply" : true
34383 Roo.util.Observable.call(this);
34386 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34387 onStartProxyDrag : function(x, y){
34388 this.fireEvent("beforeresize", this);
34390 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34392 o.enableDisplayMode("block");
34393 // all splitbars share the same overlay
34394 Roo.bootstrap.SplitBar.prototype.overlay = o;
34396 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34397 this.overlay.show();
34398 Roo.get(this.proxy).setDisplayed("block");
34399 var size = this.adapter.getElementSize(this);
34400 this.activeMinSize = this.getMinimumSize();;
34401 this.activeMaxSize = this.getMaximumSize();;
34402 var c1 = size - this.activeMinSize;
34403 var c2 = Math.max(this.activeMaxSize - size, 0);
34404 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34405 this.dd.resetConstraints();
34406 this.dd.setXConstraint(
34407 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34408 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34410 this.dd.setYConstraint(0, 0);
34412 this.dd.resetConstraints();
34413 this.dd.setXConstraint(0, 0);
34414 this.dd.setYConstraint(
34415 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34416 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34419 this.dragSpecs.startSize = size;
34420 this.dragSpecs.startPoint = [x, y];
34421 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34425 * @private Called after the drag operation by the DDProxy
34427 onEndProxyDrag : function(e){
34428 Roo.get(this.proxy).setDisplayed(false);
34429 var endPoint = Roo.lib.Event.getXY(e);
34431 this.overlay.hide();
34434 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34435 newSize = this.dragSpecs.startSize +
34436 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34437 endPoint[0] - this.dragSpecs.startPoint[0] :
34438 this.dragSpecs.startPoint[0] - endPoint[0]
34441 newSize = this.dragSpecs.startSize +
34442 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34443 endPoint[1] - this.dragSpecs.startPoint[1] :
34444 this.dragSpecs.startPoint[1] - endPoint[1]
34447 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34448 if(newSize != this.dragSpecs.startSize){
34449 if(this.fireEvent('beforeapply', this, newSize) !== false){
34450 this.adapter.setElementSize(this, newSize);
34451 this.fireEvent("moved", this, newSize);
34452 this.fireEvent("resize", this, newSize);
34458 * Get the adapter this SplitBar uses
34459 * @return The adapter object
34461 getAdapter : function(){
34462 return this.adapter;
34466 * Set the adapter this SplitBar uses
34467 * @param {Object} adapter A SplitBar adapter object
34469 setAdapter : function(adapter){
34470 this.adapter = adapter;
34471 this.adapter.init(this);
34475 * Gets the minimum size for the resizing element
34476 * @return {Number} The minimum size
34478 getMinimumSize : function(){
34479 return this.minSize;
34483 * Sets the minimum size for the resizing element
34484 * @param {Number} minSize The minimum size
34486 setMinimumSize : function(minSize){
34487 this.minSize = minSize;
34491 * Gets the maximum size for the resizing element
34492 * @return {Number} The maximum size
34494 getMaximumSize : function(){
34495 return this.maxSize;
34499 * Sets the maximum size for the resizing element
34500 * @param {Number} maxSize The maximum size
34502 setMaximumSize : function(maxSize){
34503 this.maxSize = maxSize;
34507 * Sets the initialize size for the resizing element
34508 * @param {Number} size The initial size
34510 setCurrentSize : function(size){
34511 var oldAnimate = this.animate;
34512 this.animate = false;
34513 this.adapter.setElementSize(this, size);
34514 this.animate = oldAnimate;
34518 * Destroy this splitbar.
34519 * @param {Boolean} removeEl True to remove the element
34521 destroy : function(removeEl){
34523 this.shim.remove();
34526 this.proxy.parentNode.removeChild(this.proxy);
34534 * @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.
34536 Roo.bootstrap.SplitBar.createProxy = function(dir){
34537 var proxy = new Roo.Element(document.createElement("div"));
34538 proxy.unselectable();
34539 var cls = 'roo-splitbar-proxy';
34540 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34541 document.body.appendChild(proxy.dom);
34546 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34547 * Default Adapter. It assumes the splitter and resizing element are not positioned
34548 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34550 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34553 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34554 // do nothing for now
34555 init : function(s){
34559 * Called before drag operations to get the current size of the resizing element.
34560 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34562 getElementSize : function(s){
34563 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34564 return s.resizingEl.getWidth();
34566 return s.resizingEl.getHeight();
34571 * Called after drag operations to set the size of the resizing element.
34572 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34573 * @param {Number} newSize The new size to set
34574 * @param {Function} onComplete A function to be invoked when resizing is complete
34576 setElementSize : function(s, newSize, onComplete){
34577 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34579 s.resizingEl.setWidth(newSize);
34581 onComplete(s, newSize);
34584 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34589 s.resizingEl.setHeight(newSize);
34591 onComplete(s, newSize);
34594 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34601 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34602 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34603 * Adapter that moves the splitter element to align with the resized sizing element.
34604 * Used with an absolute positioned SplitBar.
34605 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34606 * document.body, make sure you assign an id to the body element.
34608 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34609 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34610 this.container = Roo.get(container);
34613 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34614 init : function(s){
34615 this.basic.init(s);
34618 getElementSize : function(s){
34619 return this.basic.getElementSize(s);
34622 setElementSize : function(s, newSize, onComplete){
34623 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34626 moveSplitter : function(s){
34627 var yes = Roo.bootstrap.SplitBar;
34628 switch(s.placement){
34630 s.el.setX(s.resizingEl.getRight());
34633 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34636 s.el.setY(s.resizingEl.getBottom());
34639 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34646 * Orientation constant - Create a vertical SplitBar
34650 Roo.bootstrap.SplitBar.VERTICAL = 1;
34653 * Orientation constant - Create a horizontal SplitBar
34657 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34660 * Placement constant - The resizing element is to the left of the splitter element
34664 Roo.bootstrap.SplitBar.LEFT = 1;
34667 * Placement constant - The resizing element is to the right of the splitter element
34671 Roo.bootstrap.SplitBar.RIGHT = 2;
34674 * Placement constant - The resizing element is positioned above the splitter element
34678 Roo.bootstrap.SplitBar.TOP = 3;
34681 * Placement constant - The resizing element is positioned under splitter element
34685 Roo.bootstrap.SplitBar.BOTTOM = 4;
34686 Roo.namespace("Roo.bootstrap.layout");/*
34688 * Ext JS Library 1.1.1
34689 * Copyright(c) 2006-2007, Ext JS, LLC.
34691 * Originally Released Under LGPL - original licence link has changed is not relivant.
34694 * <script type="text/javascript">
34698 * @class Roo.bootstrap.layout.Manager
34699 * @extends Roo.bootstrap.Component
34700 * Base class for layout managers.
34702 Roo.bootstrap.layout.Manager = function(config)
34704 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34710 /** false to disable window resize monitoring @type Boolean */
34711 this.monitorWindowResize = true;
34716 * Fires when a layout is performed.
34717 * @param {Roo.LayoutManager} this
34721 * @event regionresized
34722 * Fires when the user resizes a region.
34723 * @param {Roo.LayoutRegion} region The resized region
34724 * @param {Number} newSize The new size (width for east/west, height for north/south)
34726 "regionresized" : true,
34728 * @event regioncollapsed
34729 * Fires when a region is collapsed.
34730 * @param {Roo.LayoutRegion} region The collapsed region
34732 "regioncollapsed" : true,
34734 * @event regionexpanded
34735 * Fires when a region is expanded.
34736 * @param {Roo.LayoutRegion} region The expanded region
34738 "regionexpanded" : true
34740 this.updating = false;
34743 this.el = Roo.get(config.el);
34749 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34754 monitorWindowResize : true,
34760 onRender : function(ct, position)
34763 this.el = Roo.get(ct);
34766 //this.fireEvent('render',this);
34770 initEvents: function()
34774 // ie scrollbar fix
34775 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34776 document.body.scroll = "no";
34777 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34778 this.el.position('relative');
34780 this.id = this.el.id;
34781 this.el.addClass("roo-layout-container");
34782 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34783 if(this.el.dom != document.body ) {
34784 this.el.on('resize', this.layout,this);
34785 this.el.on('show', this.layout,this);
34791 * Returns true if this layout is currently being updated
34792 * @return {Boolean}
34794 isUpdating : function(){
34795 return this.updating;
34799 * Suspend the LayoutManager from doing auto-layouts while
34800 * making multiple add or remove calls
34802 beginUpdate : function(){
34803 this.updating = true;
34807 * Restore auto-layouts and optionally disable the manager from performing a layout
34808 * @param {Boolean} noLayout true to disable a layout update
34810 endUpdate : function(noLayout){
34811 this.updating = false;
34817 layout: function(){
34821 onRegionResized : function(region, newSize){
34822 this.fireEvent("regionresized", region, newSize);
34826 onRegionCollapsed : function(region){
34827 this.fireEvent("regioncollapsed", region);
34830 onRegionExpanded : function(region){
34831 this.fireEvent("regionexpanded", region);
34835 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34836 * performs box-model adjustments.
34837 * @return {Object} The size as an object {width: (the width), height: (the height)}
34839 getViewSize : function()
34842 if(this.el.dom != document.body){
34843 size = this.el.getSize();
34845 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34847 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34848 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34853 * Returns the Element this layout is bound to.
34854 * @return {Roo.Element}
34856 getEl : function(){
34861 * Returns the specified region.
34862 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34863 * @return {Roo.LayoutRegion}
34865 getRegion : function(target){
34866 return this.regions[target.toLowerCase()];
34869 onWindowResize : function(){
34870 if(this.monitorWindowResize){
34877 * Ext JS Library 1.1.1
34878 * Copyright(c) 2006-2007, Ext JS, LLC.
34880 * Originally Released Under LGPL - original licence link has changed is not relivant.
34883 * <script type="text/javascript">
34886 * @class Roo.bootstrap.layout.Border
34887 * @extends Roo.bootstrap.layout.Manager
34888 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34889 * please see: examples/bootstrap/nested.html<br><br>
34891 <b>The container the layout is rendered into can be either the body element or any other element.
34892 If it is not the body element, the container needs to either be an absolute positioned element,
34893 or you will need to add "position:relative" to the css of the container. You will also need to specify
34894 the container size if it is not the body element.</b>
34897 * Create a new Border
34898 * @param {Object} config Configuration options
34900 Roo.bootstrap.layout.Border = function(config){
34901 config = config || {};
34902 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34906 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34907 if(config[region]){
34908 config[region].region = region;
34909 this.addRegion(config[region]);
34915 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34917 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34919 * Creates and adds a new region if it doesn't already exist.
34920 * @param {String} target The target region key (north, south, east, west or center).
34921 * @param {Object} config The regions config object
34922 * @return {BorderLayoutRegion} The new region
34924 addRegion : function(config)
34926 if(!this.regions[config.region]){
34927 var r = this.factory(config);
34928 this.bindRegion(r);
34930 return this.regions[config.region];
34934 bindRegion : function(r){
34935 this.regions[r.config.region] = r;
34937 r.on("visibilitychange", this.layout, this);
34938 r.on("paneladded", this.layout, this);
34939 r.on("panelremoved", this.layout, this);
34940 r.on("invalidated", this.layout, this);
34941 r.on("resized", this.onRegionResized, this);
34942 r.on("collapsed", this.onRegionCollapsed, this);
34943 r.on("expanded", this.onRegionExpanded, this);
34947 * Performs a layout update.
34949 layout : function()
34951 if(this.updating) {
34955 // render all the rebions if they have not been done alreayd?
34956 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34957 if(this.regions[region] && !this.regions[region].bodyEl){
34958 this.regions[region].onRender(this.el)
34962 var size = this.getViewSize();
34963 var w = size.width;
34964 var h = size.height;
34969 //var x = 0, y = 0;
34971 var rs = this.regions;
34972 var north = rs["north"];
34973 var south = rs["south"];
34974 var west = rs["west"];
34975 var east = rs["east"];
34976 var center = rs["center"];
34977 //if(this.hideOnLayout){ // not supported anymore
34978 //c.el.setStyle("display", "none");
34980 if(north && north.isVisible()){
34981 var b = north.getBox();
34982 var m = north.getMargins();
34983 b.width = w - (m.left+m.right);
34986 centerY = b.height + b.y + m.bottom;
34987 centerH -= centerY;
34988 north.updateBox(this.safeBox(b));
34990 if(south && south.isVisible()){
34991 var b = south.getBox();
34992 var m = south.getMargins();
34993 b.width = w - (m.left+m.right);
34995 var totalHeight = (b.height + m.top + m.bottom);
34996 b.y = h - totalHeight + m.top;
34997 centerH -= totalHeight;
34998 south.updateBox(this.safeBox(b));
35000 if(west && west.isVisible()){
35001 var b = west.getBox();
35002 var m = west.getMargins();
35003 b.height = centerH - (m.top+m.bottom);
35005 b.y = centerY + m.top;
35006 var totalWidth = (b.width + m.left + m.right);
35007 centerX += totalWidth;
35008 centerW -= totalWidth;
35009 west.updateBox(this.safeBox(b));
35011 if(east && east.isVisible()){
35012 var b = east.getBox();
35013 var m = east.getMargins();
35014 b.height = centerH - (m.top+m.bottom);
35015 var totalWidth = (b.width + m.left + m.right);
35016 b.x = w - totalWidth + m.left;
35017 b.y = centerY + m.top;
35018 centerW -= totalWidth;
35019 east.updateBox(this.safeBox(b));
35022 var m = center.getMargins();
35024 x: centerX + m.left,
35025 y: centerY + m.top,
35026 width: centerW - (m.left+m.right),
35027 height: centerH - (m.top+m.bottom)
35029 //if(this.hideOnLayout){
35030 //center.el.setStyle("display", "block");
35032 center.updateBox(this.safeBox(centerBox));
35035 this.fireEvent("layout", this);
35039 safeBox : function(box){
35040 box.width = Math.max(0, box.width);
35041 box.height = Math.max(0, box.height);
35046 * Adds a ContentPanel (or subclass) to this layout.
35047 * @param {String} target The target region key (north, south, east, west or center).
35048 * @param {Roo.ContentPanel} panel The panel to add
35049 * @return {Roo.ContentPanel} The added panel
35051 add : function(target, panel){
35053 target = target.toLowerCase();
35054 return this.regions[target].add(panel);
35058 * Remove a ContentPanel (or subclass) to this layout.
35059 * @param {String} target The target region key (north, south, east, west or center).
35060 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35061 * @return {Roo.ContentPanel} The removed panel
35063 remove : function(target, panel){
35064 target = target.toLowerCase();
35065 return this.regions[target].remove(panel);
35069 * Searches all regions for a panel with the specified id
35070 * @param {String} panelId
35071 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35073 findPanel : function(panelId){
35074 var rs = this.regions;
35075 for(var target in rs){
35076 if(typeof rs[target] != "function"){
35077 var p = rs[target].getPanel(panelId);
35087 * Searches all regions for a panel with the specified id and activates (shows) it.
35088 * @param {String/ContentPanel} panelId The panels id or the panel itself
35089 * @return {Roo.ContentPanel} The shown panel or null
35091 showPanel : function(panelId) {
35092 var rs = this.regions;
35093 for(var target in rs){
35094 var r = rs[target];
35095 if(typeof r != "function"){
35096 if(r.hasPanel(panelId)){
35097 return r.showPanel(panelId);
35105 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35106 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35109 restoreState : function(provider){
35111 provider = Roo.state.Manager;
35113 var sm = new Roo.LayoutStateManager();
35114 sm.init(this, provider);
35120 * Adds a xtype elements to the layout.
35124 xtype : 'ContentPanel',
35131 xtype : 'NestedLayoutPanel',
35137 items : [ ... list of content panels or nested layout panels.. ]
35141 * @param {Object} cfg Xtype definition of item to add.
35143 addxtype : function(cfg)
35145 // basically accepts a pannel...
35146 // can accept a layout region..!?!?
35147 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35150 // theory? children can only be panels??
35152 //if (!cfg.xtype.match(/Panel$/)) {
35157 if (typeof(cfg.region) == 'undefined') {
35158 Roo.log("Failed to add Panel, region was not set");
35162 var region = cfg.region;
35168 xitems = cfg.items;
35175 case 'Content': // ContentPanel (el, cfg)
35176 case 'Scroll': // ContentPanel (el, cfg)
35178 cfg.autoCreate = true;
35179 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35181 // var el = this.el.createChild();
35182 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35185 this.add(region, ret);
35189 case 'TreePanel': // our new panel!
35190 cfg.el = this.el.createChild();
35191 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35192 this.add(region, ret);
35197 // create a new Layout (which is a Border Layout...
35199 var clayout = cfg.layout;
35200 clayout.el = this.el.createChild();
35201 clayout.items = clayout.items || [];
35205 // replace this exitems with the clayout ones..
35206 xitems = clayout.items;
35208 // force background off if it's in center...
35209 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35210 cfg.background = false;
35212 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35215 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35216 //console.log('adding nested layout panel ' + cfg.toSource());
35217 this.add(region, ret);
35218 nb = {}; /// find first...
35223 // needs grid and region
35225 //var el = this.getRegion(region).el.createChild();
35227 *var el = this.el.createChild();
35228 // create the grid first...
35229 cfg.grid.container = el;
35230 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35233 if (region == 'center' && this.active ) {
35234 cfg.background = false;
35237 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35239 this.add(region, ret);
35241 if (cfg.background) {
35242 // render grid on panel activation (if panel background)
35243 ret.on('activate', function(gp) {
35244 if (!gp.grid.rendered) {
35245 // gp.grid.render(el);
35249 // cfg.grid.render(el);
35255 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35256 // it was the old xcomponent building that caused this before.
35257 // espeically if border is the top element in the tree.
35267 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35269 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35270 this.add(region, ret);
35274 throw "Can not add '" + cfg.xtype + "' to Border";
35280 this.beginUpdate();
35284 Roo.each(xitems, function(i) {
35285 region = nb && i.region ? i.region : false;
35287 var add = ret.addxtype(i);
35290 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35291 if (!i.background) {
35292 abn[region] = nb[region] ;
35299 // make the last non-background panel active..
35300 //if (nb) { Roo.log(abn); }
35303 for(var r in abn) {
35304 region = this.getRegion(r);
35306 // tried using nb[r], but it does not work..
35308 region.showPanel(abn[r]);
35319 factory : function(cfg)
35322 var validRegions = Roo.bootstrap.layout.Border.regions;
35324 var target = cfg.region;
35327 var r = Roo.bootstrap.layout;
35331 return new r.North(cfg);
35333 return new r.South(cfg);
35335 return new r.East(cfg);
35337 return new r.West(cfg);
35339 return new r.Center(cfg);
35341 throw 'Layout region "'+target+'" not supported.';
35348 * Ext JS Library 1.1.1
35349 * Copyright(c) 2006-2007, Ext JS, LLC.
35351 * Originally Released Under LGPL - original licence link has changed is not relivant.
35354 * <script type="text/javascript">
35358 * @class Roo.bootstrap.layout.Basic
35359 * @extends Roo.util.Observable
35360 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35361 * and does not have a titlebar, tabs or any other features. All it does is size and position
35362 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35363 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35364 * @cfg {string} region the region that it inhabits..
35365 * @cfg {bool} skipConfig skip config?
35369 Roo.bootstrap.layout.Basic = function(config){
35371 this.mgr = config.mgr;
35373 this.position = config.region;
35375 var skipConfig = config.skipConfig;
35379 * @scope Roo.BasicLayoutRegion
35383 * @event beforeremove
35384 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35385 * @param {Roo.LayoutRegion} this
35386 * @param {Roo.ContentPanel} panel The panel
35387 * @param {Object} e The cancel event object
35389 "beforeremove" : true,
35391 * @event invalidated
35392 * Fires when the layout for this region is changed.
35393 * @param {Roo.LayoutRegion} this
35395 "invalidated" : true,
35397 * @event visibilitychange
35398 * Fires when this region is shown or hidden
35399 * @param {Roo.LayoutRegion} this
35400 * @param {Boolean} visibility true or false
35402 "visibilitychange" : true,
35404 * @event paneladded
35405 * Fires when a panel is added.
35406 * @param {Roo.LayoutRegion} this
35407 * @param {Roo.ContentPanel} panel The panel
35409 "paneladded" : true,
35411 * @event panelremoved
35412 * Fires when a panel is removed.
35413 * @param {Roo.LayoutRegion} this
35414 * @param {Roo.ContentPanel} panel The panel
35416 "panelremoved" : true,
35418 * @event beforecollapse
35419 * Fires when this region before collapse.
35420 * @param {Roo.LayoutRegion} this
35422 "beforecollapse" : true,
35425 * Fires when this region is collapsed.
35426 * @param {Roo.LayoutRegion} this
35428 "collapsed" : true,
35431 * Fires when this region is expanded.
35432 * @param {Roo.LayoutRegion} this
35437 * Fires when this region is slid into view.
35438 * @param {Roo.LayoutRegion} this
35440 "slideshow" : true,
35443 * Fires when this region slides out of view.
35444 * @param {Roo.LayoutRegion} this
35446 "slidehide" : true,
35448 * @event panelactivated
35449 * Fires when a panel is activated.
35450 * @param {Roo.LayoutRegion} this
35451 * @param {Roo.ContentPanel} panel The activated panel
35453 "panelactivated" : true,
35456 * Fires when the user resizes this region.
35457 * @param {Roo.LayoutRegion} this
35458 * @param {Number} newSize The new size (width for east/west, height for north/south)
35462 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35463 this.panels = new Roo.util.MixedCollection();
35464 this.panels.getKey = this.getPanelId.createDelegate(this);
35466 this.activePanel = null;
35467 // ensure listeners are added...
35469 if (config.listeners || config.events) {
35470 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35471 listeners : config.listeners || {},
35472 events : config.events || {}
35476 if(skipConfig !== true){
35477 this.applyConfig(config);
35481 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35483 getPanelId : function(p){
35487 applyConfig : function(config){
35488 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35489 this.config = config;
35494 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35495 * the width, for horizontal (north, south) the height.
35496 * @param {Number} newSize The new width or height
35498 resizeTo : function(newSize){
35499 var el = this.el ? this.el :
35500 (this.activePanel ? this.activePanel.getEl() : null);
35502 switch(this.position){
35505 el.setWidth(newSize);
35506 this.fireEvent("resized", this, newSize);
35510 el.setHeight(newSize);
35511 this.fireEvent("resized", this, newSize);
35517 getBox : function(){
35518 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35521 getMargins : function(){
35522 return this.margins;
35525 updateBox : function(box){
35527 var el = this.activePanel.getEl();
35528 el.dom.style.left = box.x + "px";
35529 el.dom.style.top = box.y + "px";
35530 this.activePanel.setSize(box.width, box.height);
35534 * Returns the container element for this region.
35535 * @return {Roo.Element}
35537 getEl : function(){
35538 return this.activePanel;
35542 * Returns true if this region is currently visible.
35543 * @return {Boolean}
35545 isVisible : function(){
35546 return this.activePanel ? true : false;
35549 setActivePanel : function(panel){
35550 panel = this.getPanel(panel);
35551 if(this.activePanel && this.activePanel != panel){
35552 this.activePanel.setActiveState(false);
35553 this.activePanel.getEl().setLeftTop(-10000,-10000);
35555 this.activePanel = panel;
35556 panel.setActiveState(true);
35558 panel.setSize(this.box.width, this.box.height);
35560 this.fireEvent("panelactivated", this, panel);
35561 this.fireEvent("invalidated");
35565 * Show the specified panel.
35566 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35567 * @return {Roo.ContentPanel} The shown panel or null
35569 showPanel : function(panel){
35570 panel = this.getPanel(panel);
35572 this.setActivePanel(panel);
35578 * Get the active panel for this region.
35579 * @return {Roo.ContentPanel} The active panel or null
35581 getActivePanel : function(){
35582 return this.activePanel;
35586 * Add the passed ContentPanel(s)
35587 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35588 * @return {Roo.ContentPanel} The panel added (if only one was added)
35590 add : function(panel){
35591 if(arguments.length > 1){
35592 for(var i = 0, len = arguments.length; i < len; i++) {
35593 this.add(arguments[i]);
35597 if(this.hasPanel(panel)){
35598 this.showPanel(panel);
35601 var el = panel.getEl();
35602 if(el.dom.parentNode != this.mgr.el.dom){
35603 this.mgr.el.dom.appendChild(el.dom);
35605 if(panel.setRegion){
35606 panel.setRegion(this);
35608 this.panels.add(panel);
35609 el.setStyle("position", "absolute");
35610 if(!panel.background){
35611 this.setActivePanel(panel);
35612 if(this.config.initialSize && this.panels.getCount()==1){
35613 this.resizeTo(this.config.initialSize);
35616 this.fireEvent("paneladded", this, panel);
35621 * Returns true if the panel is in this region.
35622 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35623 * @return {Boolean}
35625 hasPanel : function(panel){
35626 if(typeof panel == "object"){ // must be panel obj
35627 panel = panel.getId();
35629 return this.getPanel(panel) ? true : false;
35633 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35634 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35635 * @param {Boolean} preservePanel Overrides the config preservePanel option
35636 * @return {Roo.ContentPanel} The panel that was removed
35638 remove : function(panel, preservePanel){
35639 panel = this.getPanel(panel);
35644 this.fireEvent("beforeremove", this, panel, e);
35645 if(e.cancel === true){
35648 var panelId = panel.getId();
35649 this.panels.removeKey(panelId);
35654 * Returns the panel specified or null if it's not in this region.
35655 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35656 * @return {Roo.ContentPanel}
35658 getPanel : function(id){
35659 if(typeof id == "object"){ // must be panel obj
35662 return this.panels.get(id);
35666 * Returns this regions position (north/south/east/west/center).
35669 getPosition: function(){
35670 return this.position;
35674 * Ext JS Library 1.1.1
35675 * Copyright(c) 2006-2007, Ext JS, LLC.
35677 * Originally Released Under LGPL - original licence link has changed is not relivant.
35680 * <script type="text/javascript">
35684 * @class Roo.bootstrap.layout.Region
35685 * @extends Roo.bootstrap.layout.Basic
35686 * This class represents a region in a layout manager.
35688 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35689 * @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})
35690 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35691 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35692 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35693 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35694 * @cfg {String} title The title for the region (overrides panel titles)
35695 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35696 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35697 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35698 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35699 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35700 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35701 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35702 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35703 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35704 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35706 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35707 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35708 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35709 * @cfg {Number} width For East/West panels
35710 * @cfg {Number} height For North/South panels
35711 * @cfg {Boolean} split To show the splitter
35712 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35714 * @cfg {string} cls Extra CSS classes to add to region
35716 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35717 * @cfg {string} region the region that it inhabits..
35720 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35721 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35723 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35724 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35725 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35727 Roo.bootstrap.layout.Region = function(config)
35729 this.applyConfig(config);
35731 var mgr = config.mgr;
35732 var pos = config.region;
35733 config.skipConfig = true;
35734 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35737 this.onRender(mgr.el);
35740 this.visible = true;
35741 this.collapsed = false;
35742 this.unrendered_panels = [];
35745 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35747 position: '', // set by wrapper (eg. north/south etc..)
35748 unrendered_panels : null, // unrendered panels.
35749 createBody : function(){
35750 /** This region's body element
35751 * @type Roo.Element */
35752 this.bodyEl = this.el.createChild({
35754 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35758 onRender: function(ctr, pos)
35760 var dh = Roo.DomHelper;
35761 /** This region's container element
35762 * @type Roo.Element */
35763 this.el = dh.append(ctr.dom, {
35765 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35767 /** This region's title element
35768 * @type Roo.Element */
35770 this.titleEl = dh.append(this.el.dom,
35773 unselectable: "on",
35774 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35776 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35777 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35780 this.titleEl.enableDisplayMode();
35781 /** This region's title text element
35782 * @type HTMLElement */
35783 this.titleTextEl = this.titleEl.dom.firstChild;
35784 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35786 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35787 this.closeBtn.enableDisplayMode();
35788 this.closeBtn.on("click", this.closeClicked, this);
35789 this.closeBtn.hide();
35791 this.createBody(this.config);
35792 if(this.config.hideWhenEmpty){
35794 this.on("paneladded", this.validateVisibility, this);
35795 this.on("panelremoved", this.validateVisibility, this);
35797 if(this.autoScroll){
35798 this.bodyEl.setStyle("overflow", "auto");
35800 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35802 //if(c.titlebar !== false){
35803 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35804 this.titleEl.hide();
35806 this.titleEl.show();
35807 if(this.config.title){
35808 this.titleTextEl.innerHTML = this.config.title;
35812 if(this.config.collapsed){
35813 this.collapse(true);
35815 if(this.config.hidden){
35819 if (this.unrendered_panels && this.unrendered_panels.length) {
35820 for (var i =0;i< this.unrendered_panels.length; i++) {
35821 this.add(this.unrendered_panels[i]);
35823 this.unrendered_panels = null;
35829 applyConfig : function(c)
35832 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35833 var dh = Roo.DomHelper;
35834 if(c.titlebar !== false){
35835 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35836 this.collapseBtn.on("click", this.collapse, this);
35837 this.collapseBtn.enableDisplayMode();
35839 if(c.showPin === true || this.showPin){
35840 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35841 this.stickBtn.enableDisplayMode();
35842 this.stickBtn.on("click", this.expand, this);
35843 this.stickBtn.hide();
35848 /** This region's collapsed element
35849 * @type Roo.Element */
35852 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35853 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35856 if(c.floatable !== false){
35857 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35858 this.collapsedEl.on("click", this.collapseClick, this);
35861 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35862 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35863 id: "message", unselectable: "on", style:{"float":"left"}});
35864 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35866 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35867 this.expandBtn.on("click", this.expand, this);
35871 if(this.collapseBtn){
35872 this.collapseBtn.setVisible(c.collapsible == true);
35875 this.cmargins = c.cmargins || this.cmargins ||
35876 (this.position == "west" || this.position == "east" ?
35877 {top: 0, left: 2, right:2, bottom: 0} :
35878 {top: 2, left: 0, right:0, bottom: 2});
35880 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35883 this.bottomTabs = c.tabPosition != "top";
35885 this.autoScroll = c.autoScroll || false;
35890 this.duration = c.duration || .30;
35891 this.slideDuration = c.slideDuration || .45;
35896 * Returns true if this region is currently visible.
35897 * @return {Boolean}
35899 isVisible : function(){
35900 return this.visible;
35904 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35905 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35907 //setCollapsedTitle : function(title){
35908 // title = title || " ";
35909 // if(this.collapsedTitleTextEl){
35910 // this.collapsedTitleTextEl.innerHTML = title;
35914 getBox : function(){
35916 // if(!this.collapsed){
35917 b = this.el.getBox(false, true);
35919 // b = this.collapsedEl.getBox(false, true);
35924 getMargins : function(){
35925 return this.margins;
35926 //return this.collapsed ? this.cmargins : this.margins;
35929 highlight : function(){
35930 this.el.addClass("x-layout-panel-dragover");
35933 unhighlight : function(){
35934 this.el.removeClass("x-layout-panel-dragover");
35937 updateBox : function(box)
35939 if (!this.bodyEl) {
35940 return; // not rendered yet..
35944 if(!this.collapsed){
35945 this.el.dom.style.left = box.x + "px";
35946 this.el.dom.style.top = box.y + "px";
35947 this.updateBody(box.width, box.height);
35949 this.collapsedEl.dom.style.left = box.x + "px";
35950 this.collapsedEl.dom.style.top = box.y + "px";
35951 this.collapsedEl.setSize(box.width, box.height);
35954 this.tabs.autoSizeTabs();
35958 updateBody : function(w, h)
35961 this.el.setWidth(w);
35962 w -= this.el.getBorderWidth("rl");
35963 if(this.config.adjustments){
35964 w += this.config.adjustments[0];
35967 if(h !== null && h > 0){
35968 this.el.setHeight(h);
35969 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35970 h -= this.el.getBorderWidth("tb");
35971 if(this.config.adjustments){
35972 h += this.config.adjustments[1];
35974 this.bodyEl.setHeight(h);
35976 h = this.tabs.syncHeight(h);
35979 if(this.panelSize){
35980 w = w !== null ? w : this.panelSize.width;
35981 h = h !== null ? h : this.panelSize.height;
35983 if(this.activePanel){
35984 var el = this.activePanel.getEl();
35985 w = w !== null ? w : el.getWidth();
35986 h = h !== null ? h : el.getHeight();
35987 this.panelSize = {width: w, height: h};
35988 this.activePanel.setSize(w, h);
35990 if(Roo.isIE && this.tabs){
35991 this.tabs.el.repaint();
35996 * Returns the container element for this region.
35997 * @return {Roo.Element}
35999 getEl : function(){
36004 * Hides this region.
36007 //if(!this.collapsed){
36008 this.el.dom.style.left = "-2000px";
36011 // this.collapsedEl.dom.style.left = "-2000px";
36012 // this.collapsedEl.hide();
36014 this.visible = false;
36015 this.fireEvent("visibilitychange", this, false);
36019 * Shows this region if it was previously hidden.
36022 //if(!this.collapsed){
36025 // this.collapsedEl.show();
36027 this.visible = true;
36028 this.fireEvent("visibilitychange", this, true);
36031 closeClicked : function(){
36032 if(this.activePanel){
36033 this.remove(this.activePanel);
36037 collapseClick : function(e){
36039 e.stopPropagation();
36042 e.stopPropagation();
36048 * Collapses this region.
36049 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36052 collapse : function(skipAnim, skipCheck = false){
36053 if(this.collapsed) {
36057 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36059 this.collapsed = true;
36061 this.split.el.hide();
36063 if(this.config.animate && skipAnim !== true){
36064 this.fireEvent("invalidated", this);
36065 this.animateCollapse();
36067 this.el.setLocation(-20000,-20000);
36069 this.collapsedEl.show();
36070 this.fireEvent("collapsed", this);
36071 this.fireEvent("invalidated", this);
36077 animateCollapse : function(){
36082 * Expands this region if it was previously collapsed.
36083 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36084 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36087 expand : function(e, skipAnim){
36089 e.stopPropagation();
36091 if(!this.collapsed || this.el.hasActiveFx()) {
36095 this.afterSlideIn();
36098 this.collapsed = false;
36099 if(this.config.animate && skipAnim !== true){
36100 this.animateExpand();
36104 this.split.el.show();
36106 this.collapsedEl.setLocation(-2000,-2000);
36107 this.collapsedEl.hide();
36108 this.fireEvent("invalidated", this);
36109 this.fireEvent("expanded", this);
36113 animateExpand : function(){
36117 initTabs : function()
36119 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36121 var ts = new Roo.bootstrap.panel.Tabs({
36122 el: this.bodyEl.dom,
36123 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36124 disableTooltips: this.config.disableTabTips,
36125 toolbar : this.config.toolbar
36128 if(this.config.hideTabs){
36129 ts.stripWrap.setDisplayed(false);
36132 ts.resizeTabs = this.config.resizeTabs === true;
36133 ts.minTabWidth = this.config.minTabWidth || 40;
36134 ts.maxTabWidth = this.config.maxTabWidth || 250;
36135 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36136 ts.monitorResize = false;
36137 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36138 ts.bodyEl.addClass('roo-layout-tabs-body');
36139 this.panels.each(this.initPanelAsTab, this);
36142 initPanelAsTab : function(panel){
36143 var ti = this.tabs.addTab(
36147 this.config.closeOnTab && panel.isClosable(),
36150 if(panel.tabTip !== undefined){
36151 ti.setTooltip(panel.tabTip);
36153 ti.on("activate", function(){
36154 this.setActivePanel(panel);
36157 if(this.config.closeOnTab){
36158 ti.on("beforeclose", function(t, e){
36160 this.remove(panel);
36164 panel.tabItem = ti;
36169 updatePanelTitle : function(panel, title)
36171 if(this.activePanel == panel){
36172 this.updateTitle(title);
36175 var ti = this.tabs.getTab(panel.getEl().id);
36177 if(panel.tabTip !== undefined){
36178 ti.setTooltip(panel.tabTip);
36183 updateTitle : function(title){
36184 if(this.titleTextEl && !this.config.title){
36185 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36189 setActivePanel : function(panel)
36191 panel = this.getPanel(panel);
36192 if(this.activePanel && this.activePanel != panel){
36193 if(this.activePanel.setActiveState(false) === false){
36197 this.activePanel = panel;
36198 panel.setActiveState(true);
36199 if(this.panelSize){
36200 panel.setSize(this.panelSize.width, this.panelSize.height);
36203 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36205 this.updateTitle(panel.getTitle());
36207 this.fireEvent("invalidated", this);
36209 this.fireEvent("panelactivated", this, panel);
36213 * Shows the specified panel.
36214 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36215 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36217 showPanel : function(panel)
36219 panel = this.getPanel(panel);
36222 var tab = this.tabs.getTab(panel.getEl().id);
36223 if(tab.isHidden()){
36224 this.tabs.unhideTab(tab.id);
36228 this.setActivePanel(panel);
36235 * Get the active panel for this region.
36236 * @return {Roo.ContentPanel} The active panel or null
36238 getActivePanel : function(){
36239 return this.activePanel;
36242 validateVisibility : function(){
36243 if(this.panels.getCount() < 1){
36244 this.updateTitle(" ");
36245 this.closeBtn.hide();
36248 if(!this.isVisible()){
36255 * Adds the passed ContentPanel(s) to this region.
36256 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36257 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36259 add : function(panel)
36261 if(arguments.length > 1){
36262 for(var i = 0, len = arguments.length; i < len; i++) {
36263 this.add(arguments[i]);
36268 // if we have not been rendered yet, then we can not really do much of this..
36269 if (!this.bodyEl) {
36270 this.unrendered_panels.push(panel);
36277 if(this.hasPanel(panel)){
36278 this.showPanel(panel);
36281 panel.setRegion(this);
36282 this.panels.add(panel);
36283 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36284 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36285 // and hide them... ???
36286 this.bodyEl.dom.appendChild(panel.getEl().dom);
36287 if(panel.background !== true){
36288 this.setActivePanel(panel);
36290 this.fireEvent("paneladded", this, panel);
36297 this.initPanelAsTab(panel);
36301 if(panel.background !== true){
36302 this.tabs.activate(panel.getEl().id);
36304 this.fireEvent("paneladded", this, panel);
36309 * Hides the tab for the specified panel.
36310 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36312 hidePanel : function(panel){
36313 if(this.tabs && (panel = this.getPanel(panel))){
36314 this.tabs.hideTab(panel.getEl().id);
36319 * Unhides the tab for a previously hidden panel.
36320 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36322 unhidePanel : function(panel){
36323 if(this.tabs && (panel = this.getPanel(panel))){
36324 this.tabs.unhideTab(panel.getEl().id);
36328 clearPanels : function(){
36329 while(this.panels.getCount() > 0){
36330 this.remove(this.panels.first());
36335 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36336 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36337 * @param {Boolean} preservePanel Overrides the config preservePanel option
36338 * @return {Roo.ContentPanel} The panel that was removed
36340 remove : function(panel, preservePanel)
36342 panel = this.getPanel(panel);
36347 this.fireEvent("beforeremove", this, panel, e);
36348 if(e.cancel === true){
36351 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36352 var panelId = panel.getId();
36353 this.panels.removeKey(panelId);
36355 document.body.appendChild(panel.getEl().dom);
36358 this.tabs.removeTab(panel.getEl().id);
36359 }else if (!preservePanel){
36360 this.bodyEl.dom.removeChild(panel.getEl().dom);
36362 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36363 var p = this.panels.first();
36364 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36365 tempEl.appendChild(p.getEl().dom);
36366 this.bodyEl.update("");
36367 this.bodyEl.dom.appendChild(p.getEl().dom);
36369 this.updateTitle(p.getTitle());
36371 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36372 this.setActivePanel(p);
36374 panel.setRegion(null);
36375 if(this.activePanel == panel){
36376 this.activePanel = null;
36378 if(this.config.autoDestroy !== false && preservePanel !== true){
36379 try{panel.destroy();}catch(e){}
36381 this.fireEvent("panelremoved", this, panel);
36386 * Returns the TabPanel component used by this region
36387 * @return {Roo.TabPanel}
36389 getTabs : function(){
36393 createTool : function(parentEl, className){
36394 var btn = Roo.DomHelper.append(parentEl, {
36396 cls: "x-layout-tools-button",
36399 cls: "roo-layout-tools-button-inner " + className,
36403 btn.addClassOnOver("roo-layout-tools-button-over");
36408 * Ext JS Library 1.1.1
36409 * Copyright(c) 2006-2007, Ext JS, LLC.
36411 * Originally Released Under LGPL - original licence link has changed is not relivant.
36414 * <script type="text/javascript">
36420 * @class Roo.SplitLayoutRegion
36421 * @extends Roo.LayoutRegion
36422 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36424 Roo.bootstrap.layout.Split = function(config){
36425 this.cursor = config.cursor;
36426 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36429 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36431 splitTip : "Drag to resize.",
36432 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36433 useSplitTips : false,
36435 applyConfig : function(config){
36436 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36439 onRender : function(ctr,pos) {
36441 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36442 if(!this.config.split){
36447 var splitEl = Roo.DomHelper.append(ctr.dom, {
36449 id: this.el.id + "-split",
36450 cls: "roo-layout-split roo-layout-split-"+this.position,
36453 /** The SplitBar for this region
36454 * @type Roo.SplitBar */
36455 // does not exist yet...
36456 Roo.log([this.position, this.orientation]);
36458 this.split = new Roo.bootstrap.SplitBar({
36459 dragElement : splitEl,
36460 resizingElement: this.el,
36461 orientation : this.orientation
36464 this.split.on("moved", this.onSplitMove, this);
36465 this.split.useShim = this.config.useShim === true;
36466 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36467 if(this.useSplitTips){
36468 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36470 //if(config.collapsible){
36471 // this.split.el.on("dblclick", this.collapse, this);
36474 if(typeof this.config.minSize != "undefined"){
36475 this.split.minSize = this.config.minSize;
36477 if(typeof this.config.maxSize != "undefined"){
36478 this.split.maxSize = this.config.maxSize;
36480 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36481 this.hideSplitter();
36486 getHMaxSize : function(){
36487 var cmax = this.config.maxSize || 10000;
36488 var center = this.mgr.getRegion("center");
36489 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36492 getVMaxSize : function(){
36493 var cmax = this.config.maxSize || 10000;
36494 var center = this.mgr.getRegion("center");
36495 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36498 onSplitMove : function(split, newSize){
36499 this.fireEvent("resized", this, newSize);
36503 * Returns the {@link Roo.SplitBar} for this region.
36504 * @return {Roo.SplitBar}
36506 getSplitBar : function(){
36511 this.hideSplitter();
36512 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36515 hideSplitter : function(){
36517 this.split.el.setLocation(-2000,-2000);
36518 this.split.el.hide();
36524 this.split.el.show();
36526 Roo.bootstrap.layout.Split.superclass.show.call(this);
36529 beforeSlide: function(){
36530 if(Roo.isGecko){// firefox overflow auto bug workaround
36531 this.bodyEl.clip();
36533 this.tabs.bodyEl.clip();
36535 if(this.activePanel){
36536 this.activePanel.getEl().clip();
36538 if(this.activePanel.beforeSlide){
36539 this.activePanel.beforeSlide();
36545 afterSlide : function(){
36546 if(Roo.isGecko){// firefox overflow auto bug workaround
36547 this.bodyEl.unclip();
36549 this.tabs.bodyEl.unclip();
36551 if(this.activePanel){
36552 this.activePanel.getEl().unclip();
36553 if(this.activePanel.afterSlide){
36554 this.activePanel.afterSlide();
36560 initAutoHide : function(){
36561 if(this.autoHide !== false){
36562 if(!this.autoHideHd){
36563 var st = new Roo.util.DelayedTask(this.slideIn, this);
36564 this.autoHideHd = {
36565 "mouseout": function(e){
36566 if(!e.within(this.el, true)){
36570 "mouseover" : function(e){
36576 this.el.on(this.autoHideHd);
36580 clearAutoHide : function(){
36581 if(this.autoHide !== false){
36582 this.el.un("mouseout", this.autoHideHd.mouseout);
36583 this.el.un("mouseover", this.autoHideHd.mouseover);
36587 clearMonitor : function(){
36588 Roo.get(document).un("click", this.slideInIf, this);
36591 // these names are backwards but not changed for compat
36592 slideOut : function(){
36593 if(this.isSlid || this.el.hasActiveFx()){
36596 this.isSlid = true;
36597 if(this.collapseBtn){
36598 this.collapseBtn.hide();
36600 this.closeBtnState = this.closeBtn.getStyle('display');
36601 this.closeBtn.hide();
36603 this.stickBtn.show();
36606 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36607 this.beforeSlide();
36608 this.el.setStyle("z-index", 10001);
36609 this.el.slideIn(this.getSlideAnchor(), {
36610 callback: function(){
36612 this.initAutoHide();
36613 Roo.get(document).on("click", this.slideInIf, this);
36614 this.fireEvent("slideshow", this);
36621 afterSlideIn : function(){
36622 this.clearAutoHide();
36623 this.isSlid = false;
36624 this.clearMonitor();
36625 this.el.setStyle("z-index", "");
36626 if(this.collapseBtn){
36627 this.collapseBtn.show();
36629 this.closeBtn.setStyle('display', this.closeBtnState);
36631 this.stickBtn.hide();
36633 this.fireEvent("slidehide", this);
36636 slideIn : function(cb){
36637 if(!this.isSlid || this.el.hasActiveFx()){
36641 this.isSlid = false;
36642 this.beforeSlide();
36643 this.el.slideOut(this.getSlideAnchor(), {
36644 callback: function(){
36645 this.el.setLeftTop(-10000, -10000);
36647 this.afterSlideIn();
36655 slideInIf : function(e){
36656 if(!e.within(this.el)){
36661 animateCollapse : function(){
36662 this.beforeSlide();
36663 this.el.setStyle("z-index", 20000);
36664 var anchor = this.getSlideAnchor();
36665 this.el.slideOut(anchor, {
36666 callback : function(){
36667 this.el.setStyle("z-index", "");
36668 this.collapsedEl.slideIn(anchor, {duration:.3});
36670 this.el.setLocation(-10000,-10000);
36672 this.fireEvent("collapsed", this);
36679 animateExpand : function(){
36680 this.beforeSlide();
36681 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36682 this.el.setStyle("z-index", 20000);
36683 this.collapsedEl.hide({
36686 this.el.slideIn(this.getSlideAnchor(), {
36687 callback : function(){
36688 this.el.setStyle("z-index", "");
36691 this.split.el.show();
36693 this.fireEvent("invalidated", this);
36694 this.fireEvent("expanded", this);
36722 getAnchor : function(){
36723 return this.anchors[this.position];
36726 getCollapseAnchor : function(){
36727 return this.canchors[this.position];
36730 getSlideAnchor : function(){
36731 return this.sanchors[this.position];
36734 getAlignAdj : function(){
36735 var cm = this.cmargins;
36736 switch(this.position){
36752 getExpandAdj : function(){
36753 var c = this.collapsedEl, cm = this.cmargins;
36754 switch(this.position){
36756 return [-(cm.right+c.getWidth()+cm.left), 0];
36759 return [cm.right+c.getWidth()+cm.left, 0];
36762 return [0, -(cm.top+cm.bottom+c.getHeight())];
36765 return [0, cm.top+cm.bottom+c.getHeight()];
36771 * Ext JS Library 1.1.1
36772 * Copyright(c) 2006-2007, Ext JS, LLC.
36774 * Originally Released Under LGPL - original licence link has changed is not relivant.
36777 * <script type="text/javascript">
36780 * These classes are private internal classes
36782 Roo.bootstrap.layout.Center = function(config){
36783 config.region = "center";
36784 Roo.bootstrap.layout.Region.call(this, config);
36785 this.visible = true;
36786 this.minWidth = config.minWidth || 20;
36787 this.minHeight = config.minHeight || 20;
36790 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36792 // center panel can't be hidden
36796 // center panel can't be hidden
36799 getMinWidth: function(){
36800 return this.minWidth;
36803 getMinHeight: function(){
36804 return this.minHeight;
36817 Roo.bootstrap.layout.North = function(config)
36819 config.region = 'north';
36820 config.cursor = 'n-resize';
36822 Roo.bootstrap.layout.Split.call(this, config);
36826 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36827 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36828 this.split.el.addClass("roo-layout-split-v");
36830 var size = config.initialSize || config.height;
36831 if(typeof size != "undefined"){
36832 this.el.setHeight(size);
36835 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36837 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36841 getBox : function(){
36842 if(this.collapsed){
36843 return this.collapsedEl.getBox();
36845 var box = this.el.getBox();
36847 box.height += this.split.el.getHeight();
36852 updateBox : function(box){
36853 if(this.split && !this.collapsed){
36854 box.height -= this.split.el.getHeight();
36855 this.split.el.setLeft(box.x);
36856 this.split.el.setTop(box.y+box.height);
36857 this.split.el.setWidth(box.width);
36859 if(this.collapsed){
36860 this.updateBody(box.width, null);
36862 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36870 Roo.bootstrap.layout.South = function(config){
36871 config.region = 'south';
36872 config.cursor = 's-resize';
36873 Roo.bootstrap.layout.Split.call(this, config);
36875 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36876 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36877 this.split.el.addClass("roo-layout-split-v");
36879 var size = config.initialSize || config.height;
36880 if(typeof size != "undefined"){
36881 this.el.setHeight(size);
36885 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36886 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36887 getBox : function(){
36888 if(this.collapsed){
36889 return this.collapsedEl.getBox();
36891 var box = this.el.getBox();
36893 var sh = this.split.el.getHeight();
36900 updateBox : function(box){
36901 if(this.split && !this.collapsed){
36902 var sh = this.split.el.getHeight();
36905 this.split.el.setLeft(box.x);
36906 this.split.el.setTop(box.y-sh);
36907 this.split.el.setWidth(box.width);
36909 if(this.collapsed){
36910 this.updateBody(box.width, null);
36912 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36916 Roo.bootstrap.layout.East = function(config){
36917 config.region = "east";
36918 config.cursor = "e-resize";
36919 Roo.bootstrap.layout.Split.call(this, config);
36921 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36922 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36923 this.split.el.addClass("roo-layout-split-h");
36925 var size = config.initialSize || config.width;
36926 if(typeof size != "undefined"){
36927 this.el.setWidth(size);
36930 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36931 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36932 getBox : function(){
36933 if(this.collapsed){
36934 return this.collapsedEl.getBox();
36936 var box = this.el.getBox();
36938 var sw = this.split.el.getWidth();
36945 updateBox : function(box){
36946 if(this.split && !this.collapsed){
36947 var sw = this.split.el.getWidth();
36949 this.split.el.setLeft(box.x);
36950 this.split.el.setTop(box.y);
36951 this.split.el.setHeight(box.height);
36954 if(this.collapsed){
36955 this.updateBody(null, box.height);
36957 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36961 Roo.bootstrap.layout.West = function(config){
36962 config.region = "west";
36963 config.cursor = "w-resize";
36965 Roo.bootstrap.layout.Split.call(this, config);
36967 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36968 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36969 this.split.el.addClass("roo-layout-split-h");
36973 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36974 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36976 onRender: function(ctr, pos)
36978 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36979 var size = this.config.initialSize || this.config.width;
36980 if(typeof size != "undefined"){
36981 this.el.setWidth(size);
36985 getBox : function(){
36986 if(this.collapsed){
36987 return this.collapsedEl.getBox();
36989 var box = this.el.getBox();
36991 box.width += this.split.el.getWidth();
36996 updateBox : function(box){
36997 if(this.split && !this.collapsed){
36998 var sw = this.split.el.getWidth();
37000 this.split.el.setLeft(box.x+box.width);
37001 this.split.el.setTop(box.y);
37002 this.split.el.setHeight(box.height);
37004 if(this.collapsed){
37005 this.updateBody(null, box.height);
37007 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37010 Roo.namespace("Roo.bootstrap.panel");/*
37012 * Ext JS Library 1.1.1
37013 * Copyright(c) 2006-2007, Ext JS, LLC.
37015 * Originally Released Under LGPL - original licence link has changed is not relivant.
37018 * <script type="text/javascript">
37021 * @class Roo.ContentPanel
37022 * @extends Roo.util.Observable
37023 * A basic ContentPanel element.
37024 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37025 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37026 * @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
37027 * @cfg {Boolean} closable True if the panel can be closed/removed
37028 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37029 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37030 * @cfg {Toolbar} toolbar A toolbar for this panel
37031 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37032 * @cfg {String} title The title for this panel
37033 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37034 * @cfg {String} url Calls {@link #setUrl} with this value
37035 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37036 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37037 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37038 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37039 * @cfg {Boolean} badges render the badges
37042 * Create a new ContentPanel.
37043 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37044 * @param {String/Object} config A string to set only the title or a config object
37045 * @param {String} content (optional) Set the HTML content for this panel
37046 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37048 Roo.bootstrap.panel.Content = function( config){
37050 this.tpl = config.tpl || false;
37052 var el = config.el;
37053 var content = config.content;
37055 if(config.autoCreate){ // xtype is available if this is called from factory
37058 this.el = Roo.get(el);
37059 if(!this.el && config && config.autoCreate){
37060 if(typeof config.autoCreate == "object"){
37061 if(!config.autoCreate.id){
37062 config.autoCreate.id = config.id||el;
37064 this.el = Roo.DomHelper.append(document.body,
37065 config.autoCreate, true);
37067 var elcfg = { tag: "div",
37068 cls: "roo-layout-inactive-content",
37072 elcfg.html = config.html;
37076 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37079 this.closable = false;
37080 this.loaded = false;
37081 this.active = false;
37084 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37086 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37088 this.wrapEl = this.el; //this.el.wrap();
37090 if (config.toolbar.items) {
37091 ti = config.toolbar.items ;
37092 delete config.toolbar.items ;
37096 this.toolbar.render(this.wrapEl, 'before');
37097 for(var i =0;i < ti.length;i++) {
37098 // Roo.log(['add child', items[i]]);
37099 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37101 this.toolbar.items = nitems;
37102 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37103 delete config.toolbar;
37107 // xtype created footer. - not sure if will work as we normally have to render first..
37108 if (this.footer && !this.footer.el && this.footer.xtype) {
37109 if (!this.wrapEl) {
37110 this.wrapEl = this.el.wrap();
37113 this.footer.container = this.wrapEl.createChild();
37115 this.footer = Roo.factory(this.footer, Roo);
37120 if(typeof config == "string"){
37121 this.title = config;
37123 Roo.apply(this, config);
37127 this.resizeEl = Roo.get(this.resizeEl, true);
37129 this.resizeEl = this.el;
37131 // handle view.xtype
37139 * Fires when this panel is activated.
37140 * @param {Roo.ContentPanel} this
37144 * @event deactivate
37145 * Fires when this panel is activated.
37146 * @param {Roo.ContentPanel} this
37148 "deactivate" : true,
37152 * Fires when this panel is resized if fitToFrame is true.
37153 * @param {Roo.ContentPanel} this
37154 * @param {Number} width The width after any component adjustments
37155 * @param {Number} height The height after any component adjustments
37161 * Fires when this tab is created
37162 * @param {Roo.ContentPanel} this
37173 if(this.autoScroll){
37174 this.resizeEl.setStyle("overflow", "auto");
37176 // fix randome scrolling
37177 //this.el.on('scroll', function() {
37178 // Roo.log('fix random scolling');
37179 // this.scrollTo('top',0);
37182 content = content || this.content;
37184 this.setContent(content);
37186 if(config && config.url){
37187 this.setUrl(this.url, this.params, this.loadOnce);
37192 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37194 if (this.view && typeof(this.view.xtype) != 'undefined') {
37195 this.view.el = this.el.appendChild(document.createElement("div"));
37196 this.view = Roo.factory(this.view);
37197 this.view.render && this.view.render(false, '');
37201 this.fireEvent('render', this);
37204 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37208 setRegion : function(region){
37209 this.region = region;
37210 this.setActiveClass(region && !this.background);
37214 setActiveClass: function(state)
37217 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37218 this.el.setStyle('position','relative');
37220 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37221 this.el.setStyle('position', 'absolute');
37226 * Returns the toolbar for this Panel if one was configured.
37227 * @return {Roo.Toolbar}
37229 getToolbar : function(){
37230 return this.toolbar;
37233 setActiveState : function(active)
37235 this.active = active;
37236 this.setActiveClass(active);
37238 if(this.fireEvent("deactivate", this) === false){
37243 this.fireEvent("activate", this);
37247 * Updates this panel's element
37248 * @param {String} content The new content
37249 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37251 setContent : function(content, loadScripts){
37252 this.el.update(content, loadScripts);
37255 ignoreResize : function(w, h){
37256 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37259 this.lastSize = {width: w, height: h};
37264 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37265 * @return {Roo.UpdateManager} The UpdateManager
37267 getUpdateManager : function(){
37268 return this.el.getUpdateManager();
37271 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37272 * @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:
37275 url: "your-url.php",
37276 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37277 callback: yourFunction,
37278 scope: yourObject, //(optional scope)
37281 text: "Loading...",
37286 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37287 * 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.
37288 * @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}
37289 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37290 * @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.
37291 * @return {Roo.ContentPanel} this
37294 var um = this.el.getUpdateManager();
37295 um.update.apply(um, arguments);
37301 * 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.
37302 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37303 * @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)
37304 * @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)
37305 * @return {Roo.UpdateManager} The UpdateManager
37307 setUrl : function(url, params, loadOnce){
37308 if(this.refreshDelegate){
37309 this.removeListener("activate", this.refreshDelegate);
37311 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37312 this.on("activate", this.refreshDelegate);
37313 return this.el.getUpdateManager();
37316 _handleRefresh : function(url, params, loadOnce){
37317 if(!loadOnce || !this.loaded){
37318 var updater = this.el.getUpdateManager();
37319 updater.update(url, params, this._setLoaded.createDelegate(this));
37323 _setLoaded : function(){
37324 this.loaded = true;
37328 * Returns this panel's id
37331 getId : function(){
37336 * Returns this panel's element - used by regiosn to add.
37337 * @return {Roo.Element}
37339 getEl : function(){
37340 return this.wrapEl || this.el;
37345 adjustForComponents : function(width, height)
37347 //Roo.log('adjustForComponents ');
37348 if(this.resizeEl != this.el){
37349 width -= this.el.getFrameWidth('lr');
37350 height -= this.el.getFrameWidth('tb');
37353 var te = this.toolbar.getEl();
37354 te.setWidth(width);
37355 height -= te.getHeight();
37358 var te = this.footer.getEl();
37359 te.setWidth(width);
37360 height -= te.getHeight();
37364 if(this.adjustments){
37365 width += this.adjustments[0];
37366 height += this.adjustments[1];
37368 return {"width": width, "height": height};
37371 setSize : function(width, height){
37372 if(this.fitToFrame && !this.ignoreResize(width, height)){
37373 if(this.fitContainer && this.resizeEl != this.el){
37374 this.el.setSize(width, height);
37376 var size = this.adjustForComponents(width, height);
37377 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37378 this.fireEvent('resize', this, size.width, size.height);
37383 * Returns this panel's title
37386 getTitle : function(){
37388 if (typeof(this.title) != 'object') {
37393 for (var k in this.title) {
37394 if (!this.title.hasOwnProperty(k)) {
37398 if (k.indexOf('-') >= 0) {
37399 var s = k.split('-');
37400 for (var i = 0; i<s.length; i++) {
37401 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37404 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37411 * Set this panel's title
37412 * @param {String} title
37414 setTitle : function(title){
37415 this.title = title;
37417 this.region.updatePanelTitle(this, title);
37422 * Returns true is this panel was configured to be closable
37423 * @return {Boolean}
37425 isClosable : function(){
37426 return this.closable;
37429 beforeSlide : function(){
37431 this.resizeEl.clip();
37434 afterSlide : function(){
37436 this.resizeEl.unclip();
37440 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37441 * Will fail silently if the {@link #setUrl} method has not been called.
37442 * This does not activate the panel, just updates its content.
37444 refresh : function(){
37445 if(this.refreshDelegate){
37446 this.loaded = false;
37447 this.refreshDelegate();
37452 * Destroys this panel
37454 destroy : function(){
37455 this.el.removeAllListeners();
37456 var tempEl = document.createElement("span");
37457 tempEl.appendChild(this.el.dom);
37458 tempEl.innerHTML = "";
37464 * form - if the content panel contains a form - this is a reference to it.
37465 * @type {Roo.form.Form}
37469 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37470 * This contains a reference to it.
37476 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37486 * @param {Object} cfg Xtype definition of item to add.
37490 getChildContainer: function () {
37491 return this.getEl();
37496 var ret = new Roo.factory(cfg);
37501 if (cfg.xtype.match(/^Form$/)) {
37504 //if (this.footer) {
37505 // el = this.footer.container.insertSibling(false, 'before');
37507 el = this.el.createChild();
37510 this.form = new Roo.form.Form(cfg);
37513 if ( this.form.allItems.length) {
37514 this.form.render(el.dom);
37518 // should only have one of theses..
37519 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37520 // views.. should not be just added - used named prop 'view''
37522 cfg.el = this.el.appendChild(document.createElement("div"));
37525 var ret = new Roo.factory(cfg);
37527 ret.render && ret.render(false, ''); // render blank..
37537 * @class Roo.bootstrap.panel.Grid
37538 * @extends Roo.bootstrap.panel.Content
37540 * Create a new GridPanel.
37541 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37542 * @param {Object} config A the config object
37548 Roo.bootstrap.panel.Grid = function(config)
37552 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37553 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37555 config.el = this.wrapper;
37556 //this.el = this.wrapper;
37558 if (config.container) {
37559 // ctor'ed from a Border/panel.grid
37562 this.wrapper.setStyle("overflow", "hidden");
37563 this.wrapper.addClass('roo-grid-container');
37568 if(config.toolbar){
37569 var tool_el = this.wrapper.createChild();
37570 this.toolbar = Roo.factory(config.toolbar);
37572 if (config.toolbar.items) {
37573 ti = config.toolbar.items ;
37574 delete config.toolbar.items ;
37578 this.toolbar.render(tool_el);
37579 for(var i =0;i < ti.length;i++) {
37580 // Roo.log(['add child', items[i]]);
37581 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37583 this.toolbar.items = nitems;
37585 delete config.toolbar;
37588 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37589 config.grid.scrollBody = true;;
37590 config.grid.monitorWindowResize = false; // turn off autosizing
37591 config.grid.autoHeight = false;
37592 config.grid.autoWidth = false;
37594 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37596 if (config.background) {
37597 // render grid on panel activation (if panel background)
37598 this.on('activate', function(gp) {
37599 if (!gp.grid.rendered) {
37600 gp.grid.render(this.wrapper);
37601 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37606 this.grid.render(this.wrapper);
37607 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37610 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37611 // ??? needed ??? config.el = this.wrapper;
37616 // xtype created footer. - not sure if will work as we normally have to render first..
37617 if (this.footer && !this.footer.el && this.footer.xtype) {
37619 var ctr = this.grid.getView().getFooterPanel(true);
37620 this.footer.dataSource = this.grid.dataSource;
37621 this.footer = Roo.factory(this.footer, Roo);
37622 this.footer.render(ctr);
37632 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37633 getId : function(){
37634 return this.grid.id;
37638 * Returns the grid for this panel
37639 * @return {Roo.bootstrap.Table}
37641 getGrid : function(){
37645 setSize : function(width, height){
37646 if(!this.ignoreResize(width, height)){
37647 var grid = this.grid;
37648 var size = this.adjustForComponents(width, height);
37649 var gridel = grid.getGridEl();
37650 gridel.setSize(size.width, size.height);
37652 var thd = grid.getGridEl().select('thead',true).first();
37653 var tbd = grid.getGridEl().select('tbody', true).first();
37655 tbd.setSize(width, height - thd.getHeight());
37664 beforeSlide : function(){
37665 this.grid.getView().scroller.clip();
37668 afterSlide : function(){
37669 this.grid.getView().scroller.unclip();
37672 destroy : function(){
37673 this.grid.destroy();
37675 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37680 * @class Roo.bootstrap.panel.Nest
37681 * @extends Roo.bootstrap.panel.Content
37683 * Create a new Panel, that can contain a layout.Border.
37686 * @param {Roo.BorderLayout} layout The layout for this panel
37687 * @param {String/Object} config A string to set only the title or a config object
37689 Roo.bootstrap.panel.Nest = function(config)
37691 // construct with only one argument..
37692 /* FIXME - implement nicer consturctors
37693 if (layout.layout) {
37695 layout = config.layout;
37696 delete config.layout;
37698 if (layout.xtype && !layout.getEl) {
37699 // then layout needs constructing..
37700 layout = Roo.factory(layout, Roo);
37704 config.el = config.layout.getEl();
37706 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37708 config.layout.monitorWindowResize = false; // turn off autosizing
37709 this.layout = config.layout;
37710 this.layout.getEl().addClass("roo-layout-nested-layout");
37717 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37719 setSize : function(width, height){
37720 if(!this.ignoreResize(width, height)){
37721 var size = this.adjustForComponents(width, height);
37722 var el = this.layout.getEl();
37723 if (size.height < 1) {
37724 el.setWidth(size.width);
37726 el.setSize(size.width, size.height);
37728 var touch = el.dom.offsetWidth;
37729 this.layout.layout();
37730 // ie requires a double layout on the first pass
37731 if(Roo.isIE && !this.initialized){
37732 this.initialized = true;
37733 this.layout.layout();
37738 // activate all subpanels if not currently active..
37740 setActiveState : function(active){
37741 this.active = active;
37742 this.setActiveClass(active);
37745 this.fireEvent("deactivate", this);
37749 this.fireEvent("activate", this);
37750 // not sure if this should happen before or after..
37751 if (!this.layout) {
37752 return; // should not happen..
37755 for (var r in this.layout.regions) {
37756 reg = this.layout.getRegion(r);
37757 if (reg.getActivePanel()) {
37758 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37759 reg.setActivePanel(reg.getActivePanel());
37762 if (!reg.panels.length) {
37765 reg.showPanel(reg.getPanel(0));
37774 * Returns the nested BorderLayout for this panel
37775 * @return {Roo.BorderLayout}
37777 getLayout : function(){
37778 return this.layout;
37782 * Adds a xtype elements to the layout of the nested panel
37786 xtype : 'ContentPanel',
37793 xtype : 'NestedLayoutPanel',
37799 items : [ ... list of content panels or nested layout panels.. ]
37803 * @param {Object} cfg Xtype definition of item to add.
37805 addxtype : function(cfg) {
37806 return this.layout.addxtype(cfg);
37811 * Ext JS Library 1.1.1
37812 * Copyright(c) 2006-2007, Ext JS, LLC.
37814 * Originally Released Under LGPL - original licence link has changed is not relivant.
37817 * <script type="text/javascript">
37820 * @class Roo.TabPanel
37821 * @extends Roo.util.Observable
37822 * A lightweight tab container.
37826 // basic tabs 1, built from existing content
37827 var tabs = new Roo.TabPanel("tabs1");
37828 tabs.addTab("script", "View Script");
37829 tabs.addTab("markup", "View Markup");
37830 tabs.activate("script");
37832 // more advanced tabs, built from javascript
37833 var jtabs = new Roo.TabPanel("jtabs");
37834 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37836 // set up the UpdateManager
37837 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37838 var updater = tab2.getUpdateManager();
37839 updater.setDefaultUrl("ajax1.htm");
37840 tab2.on('activate', updater.refresh, updater, true);
37842 // Use setUrl for Ajax loading
37843 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37844 tab3.setUrl("ajax2.htm", null, true);
37847 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37850 jtabs.activate("jtabs-1");
37853 * Create a new TabPanel.
37854 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37855 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37857 Roo.bootstrap.panel.Tabs = function(config){
37859 * The container element for this TabPanel.
37860 * @type Roo.Element
37862 this.el = Roo.get(config.el);
37865 if(typeof config == "boolean"){
37866 this.tabPosition = config ? "bottom" : "top";
37868 Roo.apply(this, config);
37872 if(this.tabPosition == "bottom"){
37873 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37874 this.el.addClass("roo-tabs-bottom");
37876 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37877 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37878 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37880 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37882 if(this.tabPosition != "bottom"){
37883 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37884 * @type Roo.Element
37886 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37887 this.el.addClass("roo-tabs-top");
37891 this.bodyEl.setStyle("position", "relative");
37893 this.active = null;
37894 this.activateDelegate = this.activate.createDelegate(this);
37899 * Fires when the active tab changes
37900 * @param {Roo.TabPanel} this
37901 * @param {Roo.TabPanelItem} activePanel The new active tab
37905 * @event beforetabchange
37906 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37907 * @param {Roo.TabPanel} this
37908 * @param {Object} e Set cancel to true on this object to cancel the tab change
37909 * @param {Roo.TabPanelItem} tab The tab being changed to
37911 "beforetabchange" : true
37914 Roo.EventManager.onWindowResize(this.onResize, this);
37915 this.cpad = this.el.getPadding("lr");
37916 this.hiddenCount = 0;
37919 // toolbar on the tabbar support...
37920 if (this.toolbar) {
37921 alert("no toolbar support yet");
37922 this.toolbar = false;
37924 var tcfg = this.toolbar;
37925 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37926 this.toolbar = new Roo.Toolbar(tcfg);
37927 if (Roo.isSafari) {
37928 var tbl = tcfg.container.child('table', true);
37929 tbl.setAttribute('width', '100%');
37937 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37940 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37942 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37944 tabPosition : "top",
37946 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37948 currentTabWidth : 0,
37950 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37954 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37958 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37960 preferredTabWidth : 175,
37962 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37964 resizeTabs : false,
37966 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37968 monitorResize : true,
37970 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37975 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37976 * @param {String} id The id of the div to use <b>or create</b>
37977 * @param {String} text The text for the tab
37978 * @param {String} content (optional) Content to put in the TabPanelItem body
37979 * @param {Boolean} closable (optional) True to create a close icon on the tab
37980 * @return {Roo.TabPanelItem} The created TabPanelItem
37982 addTab : function(id, text, content, closable, tpl)
37984 var item = new Roo.bootstrap.panel.TabItem({
37988 closable : closable,
37991 this.addTabItem(item);
37993 item.setContent(content);
37999 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38000 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38001 * @return {Roo.TabPanelItem}
38003 getTab : function(id){
38004 return this.items[id];
38008 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38009 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38011 hideTab : function(id){
38012 var t = this.items[id];
38015 this.hiddenCount++;
38016 this.autoSizeTabs();
38021 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38022 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38024 unhideTab : function(id){
38025 var t = this.items[id];
38027 t.setHidden(false);
38028 this.hiddenCount--;
38029 this.autoSizeTabs();
38034 * Adds an existing {@link Roo.TabPanelItem}.
38035 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38037 addTabItem : function(item){
38038 this.items[item.id] = item;
38039 this.items.push(item);
38040 // if(this.resizeTabs){
38041 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38042 // this.autoSizeTabs();
38044 // item.autoSize();
38049 * Removes a {@link Roo.TabPanelItem}.
38050 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38052 removeTab : function(id){
38053 var items = this.items;
38054 var tab = items[id];
38055 if(!tab) { return; }
38056 var index = items.indexOf(tab);
38057 if(this.active == tab && items.length > 1){
38058 var newTab = this.getNextAvailable(index);
38063 this.stripEl.dom.removeChild(tab.pnode.dom);
38064 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38065 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38067 items.splice(index, 1);
38068 delete this.items[tab.id];
38069 tab.fireEvent("close", tab);
38070 tab.purgeListeners();
38071 this.autoSizeTabs();
38074 getNextAvailable : function(start){
38075 var items = this.items;
38077 // look for a next tab that will slide over to
38078 // replace the one being removed
38079 while(index < items.length){
38080 var item = items[++index];
38081 if(item && !item.isHidden()){
38085 // if one isn't found select the previous tab (on the left)
38088 var item = items[--index];
38089 if(item && !item.isHidden()){
38097 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38098 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38100 disableTab : function(id){
38101 var tab = this.items[id];
38102 if(tab && this.active != tab){
38108 * Enables a {@link Roo.TabPanelItem} that is disabled.
38109 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38111 enableTab : function(id){
38112 var tab = this.items[id];
38117 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38118 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38119 * @return {Roo.TabPanelItem} The TabPanelItem.
38121 activate : function(id){
38122 var tab = this.items[id];
38126 if(tab == this.active || tab.disabled){
38130 this.fireEvent("beforetabchange", this, e, tab);
38131 if(e.cancel !== true && !tab.disabled){
38133 this.active.hide();
38135 this.active = this.items[id];
38136 this.active.show();
38137 this.fireEvent("tabchange", this, this.active);
38143 * Gets the active {@link Roo.TabPanelItem}.
38144 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38146 getActiveTab : function(){
38147 return this.active;
38151 * Updates the tab body element to fit the height of the container element
38152 * for overflow scrolling
38153 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38155 syncHeight : function(targetHeight){
38156 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38157 var bm = this.bodyEl.getMargins();
38158 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38159 this.bodyEl.setHeight(newHeight);
38163 onResize : function(){
38164 if(this.monitorResize){
38165 this.autoSizeTabs();
38170 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38172 beginUpdate : function(){
38173 this.updating = true;
38177 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38179 endUpdate : function(){
38180 this.updating = false;
38181 this.autoSizeTabs();
38185 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38187 autoSizeTabs : function(){
38188 var count = this.items.length;
38189 var vcount = count - this.hiddenCount;
38190 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38193 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38194 var availWidth = Math.floor(w / vcount);
38195 var b = this.stripBody;
38196 if(b.getWidth() > w){
38197 var tabs = this.items;
38198 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38199 if(availWidth < this.minTabWidth){
38200 /*if(!this.sleft){ // incomplete scrolling code
38201 this.createScrollButtons();
38204 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38207 if(this.currentTabWidth < this.preferredTabWidth){
38208 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38214 * Returns the number of tabs in this TabPanel.
38217 getCount : function(){
38218 return this.items.length;
38222 * Resizes all the tabs to the passed width
38223 * @param {Number} The new width
38225 setTabWidth : function(width){
38226 this.currentTabWidth = width;
38227 for(var i = 0, len = this.items.length; i < len; i++) {
38228 if(!this.items[i].isHidden()) {
38229 this.items[i].setWidth(width);
38235 * Destroys this TabPanel
38236 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38238 destroy : function(removeEl){
38239 Roo.EventManager.removeResizeListener(this.onResize, this);
38240 for(var i = 0, len = this.items.length; i < len; i++){
38241 this.items[i].purgeListeners();
38243 if(removeEl === true){
38244 this.el.update("");
38249 createStrip : function(container)
38251 var strip = document.createElement("nav");
38252 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38253 container.appendChild(strip);
38257 createStripList : function(strip)
38259 // div wrapper for retard IE
38260 // returns the "tr" element.
38261 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38262 //'<div class="x-tabs-strip-wrap">'+
38263 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38264 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38265 return strip.firstChild; //.firstChild.firstChild.firstChild;
38267 createBody : function(container)
38269 var body = document.createElement("div");
38270 Roo.id(body, "tab-body");
38271 //Roo.fly(body).addClass("x-tabs-body");
38272 Roo.fly(body).addClass("tab-content");
38273 container.appendChild(body);
38276 createItemBody :function(bodyEl, id){
38277 var body = Roo.getDom(id);
38279 body = document.createElement("div");
38282 //Roo.fly(body).addClass("x-tabs-item-body");
38283 Roo.fly(body).addClass("tab-pane");
38284 bodyEl.insertBefore(body, bodyEl.firstChild);
38288 createStripElements : function(stripEl, text, closable, tpl)
38290 var td = document.createElement("li"); // was td..
38293 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38296 stripEl.appendChild(td);
38298 td.className = "x-tabs-closable";
38299 if(!this.closeTpl){
38300 this.closeTpl = new Roo.Template(
38301 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38302 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38303 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38306 var el = this.closeTpl.overwrite(td, {"text": text});
38307 var close = el.getElementsByTagName("div")[0];
38308 var inner = el.getElementsByTagName("em")[0];
38309 return {"el": el, "close": close, "inner": inner};
38312 // not sure what this is..
38313 // if(!this.tabTpl){
38314 //this.tabTpl = new Roo.Template(
38315 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38316 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38318 // this.tabTpl = new Roo.Template(
38319 // '<a href="#">' +
38320 // '<span unselectable="on"' +
38321 // (this.disableTooltips ? '' : ' title="{text}"') +
38322 // ' >{text}</span></a>'
38328 var template = tpl || this.tabTpl || false;
38332 template = new Roo.Template(
38334 '<span unselectable="on"' +
38335 (this.disableTooltips ? '' : ' title="{text}"') +
38336 ' >{text}</span></a>'
38340 switch (typeof(template)) {
38344 template = new Roo.Template(template);
38350 var el = template.overwrite(td, {"text": text});
38352 var inner = el.getElementsByTagName("span")[0];
38354 return {"el": el, "inner": inner};
38362 * @class Roo.TabPanelItem
38363 * @extends Roo.util.Observable
38364 * Represents an individual item (tab plus body) in a TabPanel.
38365 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38366 * @param {String} id The id of this TabPanelItem
38367 * @param {String} text The text for the tab of this TabPanelItem
38368 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38370 Roo.bootstrap.panel.TabItem = function(config){
38372 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38373 * @type Roo.TabPanel
38375 this.tabPanel = config.panel;
38377 * The id for this TabPanelItem
38380 this.id = config.id;
38382 this.disabled = false;
38384 this.text = config.text;
38386 this.loaded = false;
38387 this.closable = config.closable;
38390 * The body element for this TabPanelItem.
38391 * @type Roo.Element
38393 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38394 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38395 this.bodyEl.setStyle("display", "block");
38396 this.bodyEl.setStyle("zoom", "1");
38397 //this.hideAction();
38399 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38401 this.el = Roo.get(els.el);
38402 this.inner = Roo.get(els.inner, true);
38403 this.textEl = Roo.get(this.el.dom.firstChild, true);
38404 this.pnode = Roo.get(els.el.parentNode, true);
38405 // this.el.on("mousedown", this.onTabMouseDown, this);
38406 this.el.on("click", this.onTabClick, this);
38408 if(config.closable){
38409 var c = Roo.get(els.close, true);
38410 c.dom.title = this.closeText;
38411 c.addClassOnOver("close-over");
38412 c.on("click", this.closeClick, this);
38418 * Fires when this tab becomes the active tab.
38419 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38420 * @param {Roo.TabPanelItem} this
38424 * @event beforeclose
38425 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38426 * @param {Roo.TabPanelItem} this
38427 * @param {Object} e Set cancel to true on this object to cancel the close.
38429 "beforeclose": true,
38432 * Fires when this tab is closed.
38433 * @param {Roo.TabPanelItem} this
38437 * @event deactivate
38438 * Fires when this tab is no longer the active tab.
38439 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38440 * @param {Roo.TabPanelItem} this
38442 "deactivate" : true
38444 this.hidden = false;
38446 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38449 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38451 purgeListeners : function(){
38452 Roo.util.Observable.prototype.purgeListeners.call(this);
38453 this.el.removeAllListeners();
38456 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38459 this.pnode.addClass("active");
38462 this.tabPanel.stripWrap.repaint();
38464 this.fireEvent("activate", this.tabPanel, this);
38468 * Returns true if this tab is the active tab.
38469 * @return {Boolean}
38471 isActive : function(){
38472 return this.tabPanel.getActiveTab() == this;
38476 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38479 this.pnode.removeClass("active");
38481 this.fireEvent("deactivate", this.tabPanel, this);
38484 hideAction : function(){
38485 this.bodyEl.hide();
38486 this.bodyEl.setStyle("position", "absolute");
38487 this.bodyEl.setLeft("-20000px");
38488 this.bodyEl.setTop("-20000px");
38491 showAction : function(){
38492 this.bodyEl.setStyle("position", "relative");
38493 this.bodyEl.setTop("");
38494 this.bodyEl.setLeft("");
38495 this.bodyEl.show();
38499 * Set the tooltip for the tab.
38500 * @param {String} tooltip The tab's tooltip
38502 setTooltip : function(text){
38503 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38504 this.textEl.dom.qtip = text;
38505 this.textEl.dom.removeAttribute('title');
38507 this.textEl.dom.title = text;
38511 onTabClick : function(e){
38512 e.preventDefault();
38513 this.tabPanel.activate(this.id);
38516 onTabMouseDown : function(e){
38517 e.preventDefault();
38518 this.tabPanel.activate(this.id);
38521 getWidth : function(){
38522 return this.inner.getWidth();
38525 setWidth : function(width){
38526 var iwidth = width - this.pnode.getPadding("lr");
38527 this.inner.setWidth(iwidth);
38528 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38529 this.pnode.setWidth(width);
38533 * Show or hide the tab
38534 * @param {Boolean} hidden True to hide or false to show.
38536 setHidden : function(hidden){
38537 this.hidden = hidden;
38538 this.pnode.setStyle("display", hidden ? "none" : "");
38542 * Returns true if this tab is "hidden"
38543 * @return {Boolean}
38545 isHidden : function(){
38546 return this.hidden;
38550 * Returns the text for this tab
38553 getText : function(){
38557 autoSize : function(){
38558 //this.el.beginMeasure();
38559 this.textEl.setWidth(1);
38561 * #2804 [new] Tabs in Roojs
38562 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38564 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38565 //this.el.endMeasure();
38569 * Sets the text for the tab (Note: this also sets the tooltip text)
38570 * @param {String} text The tab's text and tooltip
38572 setText : function(text){
38574 this.textEl.update(text);
38575 this.setTooltip(text);
38576 //if(!this.tabPanel.resizeTabs){
38577 // this.autoSize();
38581 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38583 activate : function(){
38584 this.tabPanel.activate(this.id);
38588 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38590 disable : function(){
38591 if(this.tabPanel.active != this){
38592 this.disabled = true;
38593 this.pnode.addClass("disabled");
38598 * Enables this TabPanelItem if it was previously disabled.
38600 enable : function(){
38601 this.disabled = false;
38602 this.pnode.removeClass("disabled");
38606 * Sets the content for this TabPanelItem.
38607 * @param {String} content The content
38608 * @param {Boolean} loadScripts true to look for and load scripts
38610 setContent : function(content, loadScripts){
38611 this.bodyEl.update(content, loadScripts);
38615 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38616 * @return {Roo.UpdateManager} The UpdateManager
38618 getUpdateManager : function(){
38619 return this.bodyEl.getUpdateManager();
38623 * Set a URL to be used to load the content for this TabPanelItem.
38624 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38625 * @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)
38626 * @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)
38627 * @return {Roo.UpdateManager} The UpdateManager
38629 setUrl : function(url, params, loadOnce){
38630 if(this.refreshDelegate){
38631 this.un('activate', this.refreshDelegate);
38633 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38634 this.on("activate", this.refreshDelegate);
38635 return this.bodyEl.getUpdateManager();
38639 _handleRefresh : function(url, params, loadOnce){
38640 if(!loadOnce || !this.loaded){
38641 var updater = this.bodyEl.getUpdateManager();
38642 updater.update(url, params, this._setLoaded.createDelegate(this));
38647 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38648 * Will fail silently if the setUrl method has not been called.
38649 * This does not activate the panel, just updates its content.
38651 refresh : function(){
38652 if(this.refreshDelegate){
38653 this.loaded = false;
38654 this.refreshDelegate();
38659 _setLoaded : function(){
38660 this.loaded = true;
38664 closeClick : function(e){
38667 this.fireEvent("beforeclose", this, o);
38668 if(o.cancel !== true){
38669 this.tabPanel.removeTab(this.id);
38673 * The text displayed in the tooltip for the close icon.
38676 closeText : "Close this tab"
38679 * This script refer to:
38680 * Title: International Telephone Input
38681 * Author: Jack O'Connor
38682 * Code version: v12.1.12
38683 * Availability: https://github.com/jackocnr/intl-tel-input.git
38686 Roo.bootstrap.PhoneInputData = function() {
38689 "Afghanistan (افغانستان)",
38694 "Albania (Shqipëri)",
38699 "Algeria (الجزائر)",
38724 "Antigua and Barbuda",
38734 "Armenia (Հայաստան)",
38750 "Austria (Österreich)",
38755 "Azerbaijan (Azərbaycan)",
38765 "Bahrain (البحرين)",
38770 "Bangladesh (বাংলাদেশ)",
38780 "Belarus (Беларусь)",
38785 "Belgium (België)",
38815 "Bosnia and Herzegovina (Босна и Херцеговина)",
38830 "British Indian Ocean Territory",
38835 "British Virgin Islands",
38845 "Bulgaria (България)",
38855 "Burundi (Uburundi)",
38860 "Cambodia (កម្ពុជា)",
38865 "Cameroon (Cameroun)",
38874 ["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"]
38877 "Cape Verde (Kabu Verdi)",
38882 "Caribbean Netherlands",
38893 "Central African Republic (République centrafricaine)",
38913 "Christmas Island",
38919 "Cocos (Keeling) Islands",
38930 "Comoros (جزر القمر)",
38935 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38940 "Congo (Republic) (Congo-Brazzaville)",
38960 "Croatia (Hrvatska)",
38981 "Czech Republic (Česká republika)",
38986 "Denmark (Danmark)",
39001 "Dominican Republic (República Dominicana)",
39005 ["809", "829", "849"]
39023 "Equatorial Guinea (Guinea Ecuatorial)",
39043 "Falkland Islands (Islas Malvinas)",
39048 "Faroe Islands (Føroyar)",
39069 "French Guiana (Guyane française)",
39074 "French Polynesia (Polynésie française)",
39089 "Georgia (საქართველო)",
39094 "Germany (Deutschland)",
39114 "Greenland (Kalaallit Nunaat)",
39151 "Guinea-Bissau (Guiné Bissau)",
39176 "Hungary (Magyarország)",
39181 "Iceland (Ísland)",
39201 "Iraq (العراق)",
39217 "Israel (ישראל)",
39244 "Jordan (الأردن)",
39249 "Kazakhstan (Казахстан)",
39270 "Kuwait (الكويت)",
39275 "Kyrgyzstan (Кыргызстан)",
39285 "Latvia (Latvija)",
39290 "Lebanon (لبنان)",
39305 "Libya (ليبيا)",
39315 "Lithuania (Lietuva)",
39330 "Macedonia (FYROM) (Македонија)",
39335 "Madagascar (Madagasikara)",
39365 "Marshall Islands",
39375 "Mauritania (موريتانيا)",
39380 "Mauritius (Moris)",
39401 "Moldova (Republica Moldova)",
39411 "Mongolia (Монгол)",
39416 "Montenegro (Crna Gora)",
39426 "Morocco (المغرب)",
39432 "Mozambique (Moçambique)",
39437 "Myanmar (Burma) (မြန်မာ)",
39442 "Namibia (Namibië)",
39457 "Netherlands (Nederland)",
39462 "New Caledonia (Nouvelle-Calédonie)",
39497 "North Korea (조선 민주주의 인민 공화국)",
39502 "Northern Mariana Islands",
39518 "Pakistan (پاکستان)",
39528 "Palestine (فلسطين)",
39538 "Papua New Guinea",
39580 "Réunion (La Réunion)",
39586 "Romania (România)",
39602 "Saint Barthélemy",
39613 "Saint Kitts and Nevis",
39623 "Saint Martin (Saint-Martin (partie française))",
39629 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39634 "Saint Vincent and the Grenadines",
39649 "São Tomé and Príncipe (São Tomé e Príncipe)",
39654 "Saudi Arabia (المملكة العربية السعودية)",
39659 "Senegal (Sénégal)",
39689 "Slovakia (Slovensko)",
39694 "Slovenia (Slovenija)",
39704 "Somalia (Soomaaliya)",
39714 "South Korea (대한민국)",
39719 "South Sudan (جنوب السودان)",
39729 "Sri Lanka (ශ්රී ලංකාව)",
39734 "Sudan (السودان)",
39744 "Svalbard and Jan Mayen",
39755 "Sweden (Sverige)",
39760 "Switzerland (Schweiz)",
39765 "Syria (سوريا)",
39810 "Trinidad and Tobago",
39815 "Tunisia (تونس)",
39820 "Turkey (Türkiye)",
39830 "Turks and Caicos Islands",
39840 "U.S. Virgin Islands",
39850 "Ukraine (Україна)",
39855 "United Arab Emirates (الإمارات العربية المتحدة)",
39877 "Uzbekistan (Oʻzbekiston)",
39887 "Vatican City (Città del Vaticano)",
39898 "Vietnam (Việt Nam)",
39903 "Wallis and Futuna (Wallis-et-Futuna)",
39908 "Western Sahara (الصحراء الغربية)",
39914 "Yemen (اليمن)",
39938 * This script refer to:
39939 * Title: International Telephone Input
39940 * Author: Jack O'Connor
39941 * Code version: v12.1.12
39942 * Availability: https://github.com/jackocnr/intl-tel-input.git
39946 * @class Roo.bootstrap.PhoneInput
39947 * @extends Roo.bootstrap.TriggerField
39948 * An input with International dial-code selection
39950 * @cfg {String} defaultDialCode default '+852'
39951 * @cfg {Array} preferedCountries default []
39954 * Create a new PhoneInput.
39955 * @param {Object} config Configuration options
39958 Roo.bootstrap.PhoneInput = function(config) {
39959 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39962 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39964 listWidth: undefined,
39966 selectedClass: 'active',
39968 invalidClass : "has-warning",
39970 validClass: 'has-success',
39972 allowed: '0123456789',
39977 * @cfg {String} defaultDialCode The default dial code when initializing the input
39979 defaultDialCode: '+852',
39982 * @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
39984 preferedCountries: false,
39986 getAutoCreate : function()
39988 var data = Roo.bootstrap.PhoneInputData();
39989 var align = this.labelAlign || this.parentLabelAlign();
39992 this.allCountries = [];
39993 this.dialCodeMapping = [];
39995 for (var i = 0; i < data.length; i++) {
39997 this.allCountries[i] = {
40001 priority: c[3] || 0,
40002 areaCodes: c[4] || null
40004 this.dialCodeMapping[c[2]] = {
40007 priority: c[3] || 0,
40008 areaCodes: c[4] || null
40020 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40021 maxlength: this.max_length,
40022 cls : 'form-control tel-input',
40023 autocomplete: 'new-password'
40026 var hiddenInput = {
40029 cls: 'hidden-tel-input'
40033 hiddenInput.name = this.name;
40036 if (this.disabled) {
40037 input.disabled = true;
40040 var flag_container = {
40057 cls: this.hasFeedback ? 'has-feedback' : '',
40063 cls: 'dial-code-holder',
40070 cls: 'roo-select2-container input-group',
40077 if (this.fieldLabel.length) {
40080 tooltip: 'This field is required'
40086 cls: 'control-label',
40092 html: this.fieldLabel
40095 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40101 if(this.indicatorpos == 'right') {
40102 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40109 if(align == 'left') {
40117 if(this.labelWidth > 12){
40118 label.style = "width: " + this.labelWidth + 'px';
40120 if(this.labelWidth < 13 && this.labelmd == 0){
40121 this.labelmd = this.labelWidth;
40123 if(this.labellg > 0){
40124 label.cls += ' col-lg-' + this.labellg;
40125 input.cls += ' col-lg-' + (12 - this.labellg);
40127 if(this.labelmd > 0){
40128 label.cls += ' col-md-' + this.labelmd;
40129 container.cls += ' col-md-' + (12 - this.labelmd);
40131 if(this.labelsm > 0){
40132 label.cls += ' col-sm-' + this.labelsm;
40133 container.cls += ' col-sm-' + (12 - this.labelsm);
40135 if(this.labelxs > 0){
40136 label.cls += ' col-xs-' + this.labelxs;
40137 container.cls += ' col-xs-' + (12 - this.labelxs);
40147 var settings = this;
40149 ['xs','sm','md','lg'].map(function(size){
40150 if (settings[size]) {
40151 cfg.cls += ' col-' + size + '-' + settings[size];
40155 this.store = new Roo.data.Store({
40156 proxy : new Roo.data.MemoryProxy({}),
40157 reader : new Roo.data.JsonReader({
40168 'name' : 'dialCode',
40172 'name' : 'priority',
40176 'name' : 'areaCodes',
40183 if(!this.preferedCountries) {
40184 this.preferedCountries = [
40191 var p = this.preferedCountries.reverse();
40194 for (var i = 0; i < p.length; i++) {
40195 for (var j = 0; j < this.allCountries.length; j++) {
40196 if(this.allCountries[j].iso2 == p[i]) {
40197 var t = this.allCountries[j];
40198 this.allCountries.splice(j,1);
40199 this.allCountries.unshift(t);
40205 this.store.proxy.data = {
40207 data: this.allCountries
40213 initEvents : function()
40216 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40218 this.indicator = this.indicatorEl();
40219 this.flag = this.flagEl();
40220 this.dialCodeHolder = this.dialCodeHolderEl();
40222 this.trigger = this.el.select('div.flag-box',true).first();
40223 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40228 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40229 _this.list.setWidth(lw);
40232 this.list.on('mouseover', this.onViewOver, this);
40233 this.list.on('mousemove', this.onViewMove, this);
40234 this.inputEl().on("keyup", this.onKeyUp, this);
40235 this.inputEl().on("keypress", this.onKeyPress, this);
40237 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40239 this.view = new Roo.View(this.list, this.tpl, {
40240 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40243 this.view.on('click', this.onViewClick, this);
40244 this.setValue(this.defaultDialCode);
40247 onTriggerClick : function(e)
40249 Roo.log('trigger click');
40254 if(this.isExpanded()){
40256 this.hasFocus = false;
40258 this.store.load({});
40259 this.hasFocus = true;
40264 isExpanded : function()
40266 return this.list.isVisible();
40269 collapse : function()
40271 if(!this.isExpanded()){
40275 Roo.get(document).un('mousedown', this.collapseIf, this);
40276 Roo.get(document).un('mousewheel', this.collapseIf, this);
40277 this.fireEvent('collapse', this);
40281 expand : function()
40285 if(this.isExpanded() || !this.hasFocus){
40289 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40290 this.list.setWidth(lw);
40293 this.restrictHeight();
40295 Roo.get(document).on('mousedown', this.collapseIf, this);
40296 Roo.get(document).on('mousewheel', this.collapseIf, this);
40298 this.fireEvent('expand', this);
40301 restrictHeight : function()
40303 this.list.alignTo(this.inputEl(), this.listAlign);
40304 this.list.alignTo(this.inputEl(), this.listAlign);
40307 onViewOver : function(e, t)
40309 if(this.inKeyMode){
40312 var item = this.view.findItemFromChild(t);
40315 var index = this.view.indexOf(item);
40316 this.select(index, false);
40321 onViewClick : function(view, doFocus, el, e)
40323 var index = this.view.getSelectedIndexes()[0];
40325 var r = this.store.getAt(index);
40328 this.onSelect(r, index);
40330 if(doFocus !== false && !this.blockFocus){
40331 this.inputEl().focus();
40335 onViewMove : function(e, t)
40337 this.inKeyMode = false;
40340 select : function(index, scrollIntoView)
40342 this.selectedIndex = index;
40343 this.view.select(index);
40344 if(scrollIntoView !== false){
40345 var el = this.view.getNode(index);
40347 this.list.scrollChildIntoView(el, false);
40352 createList : function()
40354 this.list = Roo.get(document.body).createChild({
40356 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40357 style: 'display:none'
40360 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40363 collapseIf : function(e)
40365 var in_combo = e.within(this.el);
40366 var in_list = e.within(this.list);
40367 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40369 if (in_combo || in_list || is_list) {
40375 onSelect : function(record, index)
40377 if(this.fireEvent('beforeselect', this, record, index) !== false){
40379 this.setFlagClass(record.data.iso2);
40380 this.setDialCode(record.data.dialCode);
40381 this.hasFocus = false;
40383 this.fireEvent('select', this, record, index);
40387 flagEl : function()
40389 var flag = this.el.select('div.flag',true).first();
40396 dialCodeHolderEl : function()
40398 var d = this.el.select('input.dial-code-holder',true).first();
40405 setDialCode : function(v)
40407 this.dialCodeHolder.dom.value = '+'+v;
40410 setFlagClass : function(n)
40412 this.flag.dom.className = 'flag '+n;
40415 getValue : function()
40417 var v = this.inputEl().getValue();
40418 if(this.dialCodeHolder) {
40419 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40424 setValue : function(v)
40426 var d = this.getDialCode(v);
40428 //invalid dial code
40429 if(v.length == 0 || !d || d.length == 0) {
40431 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40432 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40438 this.setFlagClass(this.dialCodeMapping[d].iso2);
40439 this.setDialCode(d);
40440 this.inputEl().dom.value = v.replace('+'+d,'');
40441 this.hiddenEl().dom.value = this.getValue();
40446 getDialCode : function(v)
40450 if (v.length == 0) {
40451 return this.dialCodeHolder.dom.value;
40455 if (v.charAt(0) != "+") {
40458 var numericChars = "";
40459 for (var i = 1; i < v.length; i++) {
40460 var c = v.charAt(i);
40463 if (this.dialCodeMapping[numericChars]) {
40464 dialCode = v.substr(1, i);
40466 if (numericChars.length == 4) {
40476 this.setValue(this.defaultDialCode);
40480 hiddenEl : function()
40482 return this.el.select('input.hidden-tel-input',true).first();
40485 // after setting val
40486 onKeyUp : function(e){
40487 this.setValue(this.getValue());
40490 onKeyPress : function(e){
40491 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40498 * @class Roo.bootstrap.MoneyField
40499 * @extends Roo.bootstrap.ComboBox
40500 * Bootstrap MoneyField class
40503 * Create a new MoneyField.
40504 * @param {Object} config Configuration options
40507 Roo.bootstrap.MoneyField = function(config) {
40509 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40513 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40516 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40518 allowDecimals : true,
40520 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40522 decimalSeparator : ".",
40524 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40526 decimalPrecision : 0,
40528 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40530 allowNegative : true,
40532 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40536 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40538 minValue : Number.NEGATIVE_INFINITY,
40540 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40542 maxValue : Number.MAX_VALUE,
40544 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40546 minText : "The minimum value for this field is {0}",
40548 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40550 maxText : "The maximum value for this field is {0}",
40552 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40553 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40555 nanText : "{0} is not a valid number",
40557 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40561 * @cfg {String} defaults currency of the MoneyField
40562 * value should be in lkey
40564 defaultCurrency : false,
40566 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40568 thousandsDelimiter : false,
40570 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40581 getAutoCreate : function()
40583 var align = this.labelAlign || this.parentLabelAlign();
40595 cls : 'form-control roo-money-amount-input',
40596 autocomplete: 'new-password'
40599 var hiddenInput = {
40603 cls: 'hidden-number-input'
40606 if(this.max_length) {
40607 input.maxlength = this.max_length;
40611 hiddenInput.name = this.name;
40614 if (this.disabled) {
40615 input.disabled = true;
40618 var clg = 12 - this.inputlg;
40619 var cmd = 12 - this.inputmd;
40620 var csm = 12 - this.inputsm;
40621 var cxs = 12 - this.inputxs;
40625 cls : 'row roo-money-field',
40629 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40633 cls: 'roo-select2-container input-group',
40637 cls : 'form-control roo-money-currency-input',
40638 autocomplete: 'new-password',
40640 name : this.currencyName
40644 cls : 'input-group-addon',
40658 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40662 cls: this.hasFeedback ? 'has-feedback' : '',
40673 if (this.fieldLabel.length) {
40676 tooltip: 'This field is required'
40682 cls: 'control-label',
40688 html: this.fieldLabel
40691 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40697 if(this.indicatorpos == 'right') {
40698 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40705 if(align == 'left') {
40713 if(this.labelWidth > 12){
40714 label.style = "width: " + this.labelWidth + 'px';
40716 if(this.labelWidth < 13 && this.labelmd == 0){
40717 this.labelmd = this.labelWidth;
40719 if(this.labellg > 0){
40720 label.cls += ' col-lg-' + this.labellg;
40721 input.cls += ' col-lg-' + (12 - this.labellg);
40723 if(this.labelmd > 0){
40724 label.cls += ' col-md-' + this.labelmd;
40725 container.cls += ' col-md-' + (12 - this.labelmd);
40727 if(this.labelsm > 0){
40728 label.cls += ' col-sm-' + this.labelsm;
40729 container.cls += ' col-sm-' + (12 - this.labelsm);
40731 if(this.labelxs > 0){
40732 label.cls += ' col-xs-' + this.labelxs;
40733 container.cls += ' col-xs-' + (12 - this.labelxs);
40744 var settings = this;
40746 ['xs','sm','md','lg'].map(function(size){
40747 if (settings[size]) {
40748 cfg.cls += ' col-' + size + '-' + settings[size];
40755 initEvents : function()
40757 this.indicator = this.indicatorEl();
40759 this.initCurrencyEvent();
40761 this.initNumberEvent();
40764 initCurrencyEvent : function()
40767 throw "can not find store for combo";
40770 this.store = Roo.factory(this.store, Roo.data);
40771 this.store.parent = this;
40775 this.triggerEl = this.el.select('.input-group-addon', true).first();
40777 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40782 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40783 _this.list.setWidth(lw);
40786 this.list.on('mouseover', this.onViewOver, this);
40787 this.list.on('mousemove', this.onViewMove, this);
40788 this.list.on('scroll', this.onViewScroll, this);
40791 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40794 this.view = new Roo.View(this.list, this.tpl, {
40795 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40798 this.view.on('click', this.onViewClick, this);
40800 this.store.on('beforeload', this.onBeforeLoad, this);
40801 this.store.on('load', this.onLoad, this);
40802 this.store.on('loadexception', this.onLoadException, this);
40804 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40805 "up" : function(e){
40806 this.inKeyMode = true;
40810 "down" : function(e){
40811 if(!this.isExpanded()){
40812 this.onTriggerClick();
40814 this.inKeyMode = true;
40819 "enter" : function(e){
40822 if(this.fireEvent("specialkey", this, e)){
40823 this.onViewClick(false);
40829 "esc" : function(e){
40833 "tab" : function(e){
40836 if(this.fireEvent("specialkey", this, e)){
40837 this.onViewClick(false);
40845 doRelay : function(foo, bar, hname){
40846 if(hname == 'down' || this.scope.isExpanded()){
40847 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40855 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40859 initNumberEvent : function(e)
40861 this.inputEl().on("keydown" , this.fireKey, this);
40862 this.inputEl().on("focus", this.onFocus, this);
40863 this.inputEl().on("blur", this.onBlur, this);
40865 this.inputEl().relayEvent('keyup', this);
40867 if(this.indicator){
40868 this.indicator.addClass('invisible');
40871 this.originalValue = this.getValue();
40873 if(this.validationEvent == 'keyup'){
40874 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40875 this.inputEl().on('keyup', this.filterValidation, this);
40877 else if(this.validationEvent !== false){
40878 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40881 if(this.selectOnFocus){
40882 this.on("focus", this.preFocus, this);
40885 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40886 this.inputEl().on("keypress", this.filterKeys, this);
40888 this.inputEl().relayEvent('keypress', this);
40891 var allowed = "0123456789";
40893 if(this.allowDecimals){
40894 allowed += this.decimalSeparator;
40897 if(this.allowNegative){
40901 if(this.thousandsDelimiter) {
40905 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40907 var keyPress = function(e){
40909 var k = e.getKey();
40911 var c = e.getCharCode();
40914 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40915 allowed.indexOf(String.fromCharCode(c)) === -1
40921 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40925 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40930 this.inputEl().on("keypress", keyPress, this);
40934 onTriggerClick : function(e)
40941 this.loadNext = false;
40943 if(this.isExpanded()){
40948 this.hasFocus = true;
40950 if(this.triggerAction == 'all') {
40951 this.doQuery(this.allQuery, true);
40955 this.doQuery(this.getRawValue());
40958 getCurrency : function()
40960 var v = this.currencyEl().getValue();
40965 restrictHeight : function()
40967 this.list.alignTo(this.currencyEl(), this.listAlign);
40968 this.list.alignTo(this.currencyEl(), this.listAlign);
40971 onViewClick : function(view, doFocus, el, e)
40973 var index = this.view.getSelectedIndexes()[0];
40975 var r = this.store.getAt(index);
40978 this.onSelect(r, index);
40982 onSelect : function(record, index){
40984 if(this.fireEvent('beforeselect', this, record, index) !== false){
40986 this.setFromCurrencyData(index > -1 ? record.data : false);
40990 this.fireEvent('select', this, record, index);
40994 setFromCurrencyData : function(o)
40998 this.lastCurrency = o;
41000 if (this.currencyField) {
41001 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41003 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41006 this.lastSelectionText = currency;
41008 //setting default currency
41009 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41010 this.setCurrency(this.defaultCurrency);
41014 this.setCurrency(currency);
41017 setFromData : function(o)
41021 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41023 this.setFromCurrencyData(c);
41028 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41030 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41033 this.setValue(value);
41037 setCurrency : function(v)
41039 this.currencyValue = v;
41042 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41047 setValue : function(v)
41049 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41055 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41057 this.inputEl().dom.value = (v == '') ? '' :
41058 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41060 if(!this.allowZero && v === '0') {
41061 this.hiddenEl().dom.value = '';
41062 this.inputEl().dom.value = '';
41069 getRawValue : function()
41071 var v = this.inputEl().getValue();
41076 getValue : function()
41078 return this.fixPrecision(this.parseValue(this.getRawValue()));
41081 parseValue : function(value)
41083 if(this.thousandsDelimiter) {
41085 r = new RegExp(",", "g");
41086 value = value.replace(r, "");
41089 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41090 return isNaN(value) ? '' : value;
41094 fixPrecision : function(value)
41096 if(this.thousandsDelimiter) {
41098 r = new RegExp(",", "g");
41099 value = value.replace(r, "");
41102 var nan = isNaN(value);
41104 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41105 return nan ? '' : value;
41107 return parseFloat(value).toFixed(this.decimalPrecision);
41110 decimalPrecisionFcn : function(v)
41112 return Math.floor(v);
41115 validateValue : function(value)
41117 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41121 var num = this.parseValue(value);
41124 this.markInvalid(String.format(this.nanText, value));
41128 if(num < this.minValue){
41129 this.markInvalid(String.format(this.minText, this.minValue));
41133 if(num > this.maxValue){
41134 this.markInvalid(String.format(this.maxText, this.maxValue));
41141 validate : function()
41143 if(this.disabled || this.allowBlank){
41148 var currency = this.getCurrency();
41150 if(this.validateValue(this.getRawValue()) && currency.length){
41155 this.markInvalid();
41159 getName: function()
41164 beforeBlur : function()
41170 var v = this.parseValue(this.getRawValue());
41177 onBlur : function()
41181 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41182 //this.el.removeClass(this.focusClass);
41185 this.hasFocus = false;
41187 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41191 var v = this.getValue();
41193 if(String(v) !== String(this.startValue)){
41194 this.fireEvent('change', this, v, this.startValue);
41197 this.fireEvent("blur", this);
41200 inputEl : function()
41202 return this.el.select('.roo-money-amount-input', true).first();
41205 currencyEl : function()
41207 return this.el.select('.roo-money-currency-input', true).first();
41210 hiddenEl : function()
41212 return this.el.select('input.hidden-number-input',true).first();