2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fs
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
661 preventDefault: true,
669 getAutoCreate : function(){
677 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
683 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685 if (this.toggle == true) {
688 cls: 'slider-frame roo-button',
693 'data-off-text':'OFF',
694 cls: 'slider-button',
700 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' '+this.weight;
710 cfg["aria-hidden"] = true;
712 cfg.html = "×";
718 if (this.theme==='default') {
719 cfg.cls = 'btn roo-button';
721 //if (this.parentType != 'Navbar') {
722 this.weight = this.weight.length ? this.weight : 'default';
724 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728 cfg.cls += ' btn-' + outline + weight;
729 if (this.weight == 'default') {
731 cfg.cls += ' btn-' + this.weight;
734 } else if (this.theme==='glow') {
737 cfg.cls = 'btn-glow roo-button';
739 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741 cfg.cls += ' ' + this.weight;
747 this.cls += ' inverse';
751 if (this.active || this.pressed === true) {
752 cfg.cls += ' active';
756 cfg.disabled = 'disabled';
760 Roo.log('changing to ul' );
762 this.glyphicon = 'caret';
763 if (Roo.bootstrap.version == 4) {
764 this.fa = 'caret-down';
769 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771 //gsRoo.log(this.parentType);
772 if (this.parentType === 'Navbar' && !this.parent().bar) {
773 Roo.log('changing to li?');
782 href : this.href || '#'
785 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
786 cfg.cls += ' dropdown';
793 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
795 if (this.glyphicon) {
796 cfg.html = ' ' + cfg.html;
801 cls: 'glyphicon glyphicon-' + this.glyphicon
806 cfg.html = ' ' + cfg.html;
811 cls: 'fa fas fa-' + this.fa
821 // cfg.cls='btn roo-button';
825 var value = cfg.html;
830 cls: 'glyphicon glyphicon-' + this.glyphicon,
837 cls: 'fa fas fa-' + this.fa,
842 var bw = this.badge_weight.length ? this.badge_weight :
843 (this.weight.length ? this.weight : 'secondary');
844 bw = bw == 'default' ? 'secondary' : bw;
850 cls: 'badge badge-' + bw,
859 cfg.cls += ' dropdown';
860 cfg.html = typeof(cfg.html) != 'undefined' ?
861 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
864 if (cfg.tag !== 'a' && this.href !== '') {
865 throw "Tag must be a to set href.";
866 } else if (this.href.length > 0) {
867 cfg.href = this.href;
870 if(this.removeClass){
875 cfg.target = this.target;
880 initEvents: function() {
881 // Roo.log('init events?');
882 // Roo.log(this.el.dom);
885 if (typeof (this.menu) != 'undefined') {
886 this.menu.parentType = this.xtype;
887 this.menu.triggerEl = this.el;
888 this.addxtype(Roo.apply({}, this.menu));
892 if (this.el.hasClass('roo-button')) {
893 this.el.on('click', this.onClick, this);
895 this.el.select('.roo-button').on('click', this.onClick, this);
898 if(this.removeClass){
899 this.el.on('click', this.onClick, this);
902 this.el.enableDisplayMode();
905 onClick : function(e)
911 Roo.log('button on click ');
912 if(this.preventDefault){
916 if (this.pressed === true || this.pressed === false) {
917 this.toggleActive(e);
921 this.fireEvent('click', this, e);
925 * Enables this button
929 this.disabled = false;
930 this.el.removeClass('disabled');
934 * Disable this button
938 this.disabled = true;
939 this.el.addClass('disabled');
942 * sets the active state on/off,
943 * @param {Boolean} state (optional) Force a particular state
945 setActive : function(v) {
947 this.el[v ? 'addClass' : 'removeClass']('active');
951 * toggles the current active state
953 toggleActive : function(e)
955 this.setActive(!this.pressed);
956 this.fireEvent('toggle', this, e, !this.pressed);
959 * get the current active state
960 * @return {boolean} true if it's active
962 isActive : function()
964 return this.el.hasClass('active');
967 * set the text of the first selected button
969 setText : function(str)
971 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
974 * get the text of the first selected button
978 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
981 setWeight : function(str)
983 this.el.removeClass(this.weightClass);
985 var outline = this.outline ? 'outline-' : '';
986 if (str == 'default') {
987 this.el.addClass('btn-default btn-outline-secondary');
990 this.el.addClass('btn-' + outline + str);
1004 * @class Roo.bootstrap.Column
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Column class
1007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019 * @cfg {String} fa (ban|check|...) font awesome icon
1020 * @cfg {Number} fasize (1|2|....) font awsome size
1022 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {String} html content of column.
1027 * Create a new Column
1028 * @param {Object} config The config object
1031 Roo.bootstrap.Column = function(config){
1032 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1054 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1062 ['xs','sm','md','lg'].map(function(size){
1063 //Roo.log( size + ':' + settings[size]);
1065 if (settings[size+'off'] !== false) {
1066 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069 if (settings[size] === false) {
1073 if (!settings[size]) { // 0 = hidden
1074 cfg.cls += ' hidden-' + size;
1077 cfg.cls += ' col-' + size + '-' + settings[size];
1082 cfg.cls += ' hidden';
1085 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086 cfg.cls +=' alert alert-' + this.alert;
1090 if (this.html.length) {
1091 cfg.html = this.html;
1095 if (this.fasize > 1) {
1096 fasize = ' fa-' + this.fasize + 'x';
1098 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1103 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1122 * @class Roo.bootstrap.Container
1123 * @extends Roo.bootstrap.Component
1124 * Bootstrap Container class
1125 * @cfg {Boolean} jumbotron is it a jumbotron element
1126 * @cfg {String} html content of element
1127 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1129 * @cfg {String} header content of header (for panel)
1130 * @cfg {String} footer content of footer (for panel)
1131 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132 * @cfg {String} tag (header|aside|section) type of HTML tag.
1133 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134 * @cfg {String} fa font awesome icon
1135 * @cfg {String} icon (info-sign|check|...) glyphicon name
1136 * @cfg {Boolean} hidden (true|false) hide the element
1137 * @cfg {Boolean} expandable (true|false) default false
1138 * @cfg {Boolean} expanded (true|false) default true
1139 * @cfg {String} rheader contet on the right of header
1140 * @cfg {Boolean} clickable (true|false) default false
1144 * Create a new Container
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Container = function(config){
1149 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1155 * After the panel has been expand
1157 * @param {Roo.bootstrap.Container} this
1162 * After the panel has been collapsed
1164 * @param {Roo.bootstrap.Container} this
1169 * When a element is chick
1170 * @param {Roo.bootstrap.Container} this
1171 * @param {Roo.EventObject} e
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1195 getChildContainer : function() {
1201 if (this.panel.length) {
1202 return this.el.select('.panel-body',true).first();
1209 getAutoCreate : function(){
1212 tag : this.tag || 'div',
1216 if (this.jumbotron) {
1217 cfg.cls = 'jumbotron';
1222 // - this is applied by the parent..
1224 // cfg.cls = this.cls + '';
1227 if (this.sticky.length) {
1229 var bd = Roo.get(document.body);
1230 if (!bd.hasClass('bootstrap-sticky')) {
1231 bd.addClass('bootstrap-sticky');
1232 Roo.select('html',true).setStyle('height', '100%');
1235 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239 if (this.well.length) {
1240 switch (this.well) {
1243 cfg.cls +=' well well-' +this.well;
1252 cfg.cls += ' hidden';
1256 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257 cfg.cls +=' alert alert-' + this.alert;
1262 if (this.panel.length) {
1263 cfg.cls += ' panel panel-' + this.panel;
1265 if (this.header.length) {
1269 if(this.expandable){
1271 cfg.cls = cfg.cls + ' expandable';
1275 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1283 cls : 'panel-title',
1284 html : (this.expandable ? ' ' : '') + this.header
1288 cls: 'panel-header-right',
1294 cls : 'panel-heading',
1295 style : this.expandable ? 'cursor: pointer' : '',
1303 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1308 if (this.footer.length) {
1310 cls : 'panel-footer',
1319 body.html = this.html || cfg.html;
1320 // prefix with the icons..
1322 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1325 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1330 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331 cfg.cls = 'container';
1337 initEvents: function()
1339 if(this.expandable){
1340 var headerEl = this.headerEl();
1343 headerEl.on('click', this.onToggleClick, this);
1348 this.el.on('click', this.onClick, this);
1353 onToggleClick : function()
1355 var headerEl = this.headerEl();
1371 if(this.fireEvent('expand', this)) {
1373 this.expanded = true;
1375 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377 this.el.select('.panel-body',true).first().removeClass('hide');
1379 var toggleEl = this.toggleEl();
1385 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1390 collapse : function()
1392 if(this.fireEvent('collapse', this)) {
1394 this.expanded = false;
1396 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397 this.el.select('.panel-body',true).first().addClass('hide');
1399 var toggleEl = this.toggleEl();
1405 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409 toggleEl : function()
1411 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415 return this.el.select('.panel-heading .fa',true).first();
1418 headerEl : function()
1420 if(!this.el || !this.panel.length || !this.header.length){
1424 return this.el.select('.panel-heading',true).first()
1429 if(!this.el || !this.panel.length){
1433 return this.el.select('.panel-body',true).first()
1436 titleEl : function()
1438 if(!this.el || !this.panel.length || !this.header.length){
1442 return this.el.select('.panel-title',true).first();
1445 setTitle : function(v)
1447 var titleEl = this.titleEl();
1453 titleEl.dom.innerHTML = v;
1456 getTitle : function()
1459 var titleEl = this.titleEl();
1465 return titleEl.dom.innerHTML;
1468 setRightTitle : function(v)
1470 var t = this.el.select('.panel-header-right',true).first();
1476 t.dom.innerHTML = v;
1479 onClick : function(e)
1483 this.fireEvent('click', this, e);
1496 * @class Roo.bootstrap.Img
1497 * @extends Roo.bootstrap.Component
1498 * Bootstrap Img class
1499 * @cfg {Boolean} imgResponsive false | true
1500 * @cfg {String} border rounded | circle | thumbnail
1501 * @cfg {String} src image source
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505 * @cfg {String} xsUrl xs image source
1506 * @cfg {String} smUrl sm image source
1507 * @cfg {String} mdUrl md image source
1508 * @cfg {String} lgUrl lg image source
1511 * Create a new Input
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Img = function(config){
1516 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1522 * The img click event for the img.
1523 * @param {Roo.EventObject} e
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1531 imgResponsive: true,
1541 getAutoCreate : function()
1543 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544 return this.createSingleImg();
1549 cls: 'roo-image-responsive-group',
1554 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556 if(!_this[size + 'Url']){
1562 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563 html: _this.html || cfg.html,
1564 src: _this[size + 'Url']
1567 img.cls += ' roo-image-responsive-' + size;
1569 var s = ['xs', 'sm', 'md', 'lg'];
1571 s.splice(s.indexOf(size), 1);
1573 Roo.each(s, function(ss){
1574 img.cls += ' hidden-' + ss;
1577 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578 cfg.cls += ' img-' + _this.border;
1582 cfg.alt = _this.alt;
1595 a.target = _this.target;
1599 cfg.cn.push((_this.href) ? a : img);
1606 createSingleImg : function()
1610 cls: (this.imgResponsive) ? 'img-responsive' : '',
1612 src : 'about:blank' // just incase src get's set to undefined?!?
1615 cfg.html = this.html || cfg.html;
1617 cfg.src = this.src || cfg.src;
1619 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620 cfg.cls += ' img-' + this.border;
1637 a.target = this.target;
1642 return (this.href) ? a : cfg;
1645 initEvents: function()
1648 this.el.on('click', this.onClick, this);
1653 onClick : function(e)
1655 Roo.log('img onclick');
1656 this.fireEvent('click', this, e);
1659 * Sets the url of the image - used to update it
1660 * @param {String} url the url of the image
1663 setSrc : function(url)
1667 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668 this.el.dom.src = url;
1672 this.el.select('img', true).first().dom.src = url;
1688 * @class Roo.bootstrap.Link
1689 * @extends Roo.bootstrap.Component
1690 * Bootstrap Link Class
1691 * @cfg {String} alt image alternative text
1692 * @cfg {String} href a tag href
1693 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694 * @cfg {String} html the content of the link.
1695 * @cfg {String} anchor name for the anchor link
1696 * @cfg {String} fa - favicon
1698 * @cfg {Boolean} preventDefault (true | false) default false
1702 * Create a new Input
1703 * @param {Object} config The config object
1706 Roo.bootstrap.Link = function(config){
1707 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1713 * The img click event for the img.
1714 * @param {Roo.EventObject} e
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1724 preventDefault: false,
1730 getAutoCreate : function()
1732 var html = this.html || '';
1734 if (this.fa !== false) {
1735 html = '<i class="fa fa-' + this.fa + '"></i>';
1740 // anchor's do not require html/href...
1741 if (this.anchor === false) {
1743 cfg.href = this.href || '#';
1745 cfg.name = this.anchor;
1746 if (this.html !== false || this.fa !== false) {
1749 if (this.href !== false) {
1750 cfg.href = this.href;
1754 if(this.alt !== false){
1759 if(this.target !== false) {
1760 cfg.target = this.target;
1766 initEvents: function() {
1768 if(!this.href || this.preventDefault){
1769 this.el.on('click', this.onClick, this);
1773 onClick : function(e)
1775 if(this.preventDefault){
1778 //Roo.log('img onclick');
1779 this.fireEvent('click', this, e);
1792 * @class Roo.bootstrap.Header
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Header class
1795 * @cfg {String} html content of header
1796 * @cfg {Number} level (1|2|3|4|5|6) default 1
1799 * Create a new Header
1800 * @param {Object} config The config object
1804 Roo.bootstrap.Header = function(config){
1805 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1816 getAutoCreate : function(){
1821 tag: 'h' + (1 *this.level),
1822 html: this.html || ''
1834 * Ext JS Library 1.1.1
1835 * Copyright(c) 2006-2007, Ext JS, LLC.
1837 * Originally Released Under LGPL - original licence link has changed is not relivant.
1840 * <script type="text/javascript">
1844 * @class Roo.bootstrap.MenuMgr
1845 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1848 Roo.bootstrap.MenuMgr = function(){
1849 var menus, active, groups = {}, attached = false, lastShow = new Date();
1851 // private - called when first menu is created
1854 active = new Roo.util.MixedCollection();
1855 Roo.get(document).addKeyListener(27, function(){
1856 if(active.length > 0){
1864 if(active && active.length > 0){
1865 var c = active.clone();
1875 if(active.length < 1){
1876 Roo.get(document).un("mouseup", onMouseDown);
1884 var last = active.last();
1885 lastShow = new Date();
1888 Roo.get(document).on("mouseup", onMouseDown);
1893 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894 m.parentMenu.activeChild = m;
1895 }else if(last && last.isVisible()){
1896 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1901 function onBeforeHide(m){
1903 m.activeChild.hide();
1905 if(m.autoHideTimer){
1906 clearTimeout(m.autoHideTimer);
1907 delete m.autoHideTimer;
1912 function onBeforeShow(m){
1913 var pm = m.parentMenu;
1914 if(!pm && !m.allowOtherMenus){
1916 }else if(pm && pm.activeChild && active != m){
1917 pm.activeChild.hide();
1921 // private this should really trigger on mouseup..
1922 function onMouseDown(e){
1923 Roo.log("on Mouse Up");
1925 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926 Roo.log("MenuManager hideAll");
1935 function onBeforeCheck(mi, state){
1937 var g = groups[mi.group];
1938 for(var i = 0, l = g.length; i < l; i++){
1940 g[i].setChecked(false);
1949 * Hides all menus that are currently visible
1951 hideAll : function(){
1956 register : function(menu){
1960 menus[menu.id] = menu;
1961 menu.on("beforehide", onBeforeHide);
1962 menu.on("hide", onHide);
1963 menu.on("beforeshow", onBeforeShow);
1964 menu.on("show", onShow);
1966 if(g && menu.events["checkchange"]){
1970 groups[g].push(menu);
1971 menu.on("checkchange", onCheck);
1976 * Returns a {@link Roo.menu.Menu} object
1977 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978 * be used to generate and return a new Menu instance.
1980 get : function(menu){
1981 if(typeof menu == "string"){ // menu id
1983 }else if(menu.events){ // menu instance
1986 /*else if(typeof menu.length == 'number'){ // array of menu items?
1987 return new Roo.bootstrap.Menu({items:menu});
1988 }else{ // otherwise, must be a config
1989 return new Roo.bootstrap.Menu(menu);
1996 unregister : function(menu){
1997 delete menus[menu.id];
1998 menu.un("beforehide", onBeforeHide);
1999 menu.un("hide", onHide);
2000 menu.un("beforeshow", onBeforeShow);
2001 menu.un("show", onShow);
2003 if(g && menu.events["checkchange"]){
2004 groups[g].remove(menu);
2005 menu.un("checkchange", onCheck);
2010 registerCheckable : function(menuItem){
2011 var g = menuItem.group;
2016 groups[g].push(menuItem);
2017 menuItem.on("beforecheckchange", onBeforeCheck);
2022 unregisterCheckable : function(menuItem){
2023 var g = menuItem.group;
2025 groups[g].remove(menuItem);
2026 menuItem.un("beforecheckchange", onBeforeCheck);
2038 * @class Roo.bootstrap.Menu
2039 * @extends Roo.bootstrap.Component
2040 * Bootstrap Menu class - container for MenuItems
2041 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042 * @cfg {bool} hidden if the menu should be hidden when rendered.
2043 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2044 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2048 * @param {Object} config The config object
2052 Roo.bootstrap.Menu = function(config){
2053 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054 if (this.registerMenu && this.type != 'treeview') {
2055 Roo.bootstrap.MenuMgr.register(this);
2062 * Fires before this menu is displayed
2063 * @param {Roo.menu.Menu} this
2068 * Fires before this menu is hidden
2069 * @param {Roo.menu.Menu} this
2074 * Fires after this menu is displayed
2075 * @param {Roo.menu.Menu} this
2080 * Fires after this menu is hidden
2081 * @param {Roo.menu.Menu} this
2086 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087 * @param {Roo.menu.Menu} this
2088 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089 * @param {Roo.EventObject} e
2094 * Fires when the mouse is hovering over this menu
2095 * @param {Roo.menu.Menu} this
2096 * @param {Roo.EventObject} e
2097 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2102 * Fires when the mouse exits this menu
2103 * @param {Roo.menu.Menu} this
2104 * @param {Roo.EventObject} e
2105 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2110 * Fires when a menu item contained in this menu is clicked
2111 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112 * @param {Roo.EventObject} e
2116 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2123 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2126 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128 registerMenu : true,
2130 menuItems :false, // stores the menu items..
2140 getChildContainer : function() {
2144 getAutoCreate : function(){
2146 //if (['right'].indexOf(this.align)!==-1) {
2147 // cfg.cn[1].cls += ' pull-right'
2153 cls : 'dropdown-menu' ,
2154 style : 'z-index:1000'
2158 if (this.type === 'submenu') {
2159 cfg.cls = 'submenu active';
2161 if (this.type === 'treeview') {
2162 cfg.cls = 'treeview-menu';
2167 initEvents : function() {
2169 // Roo.log("ADD event");
2170 // Roo.log(this.triggerEl.dom);
2172 this.triggerEl.on('click', this.onTriggerClick, this);
2174 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2177 if (this.triggerEl.hasClass('nav-item')) {
2178 // dropdown toggle on the 'a' in BS4?
2179 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181 this.triggerEl.addClass('dropdown-toggle');
2184 this.el.on('touchstart' , this.onTouch, this);
2186 this.el.on('click' , this.onClick, this);
2188 this.el.on("mouseover", this.onMouseOver, this);
2189 this.el.on("mouseout", this.onMouseOut, this);
2193 findTargetItem : function(e)
2195 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2199 //Roo.log(t); Roo.log(t.id);
2201 //Roo.log(this.menuitems);
2202 return this.menuitems.get(t.id);
2204 //return this.items.get(t.menuItemId);
2210 onTouch : function(e)
2212 Roo.log("menu.onTouch");
2213 //e.stopEvent(); this make the user popdown broken
2217 onClick : function(e)
2219 Roo.log("menu.onClick");
2221 var t = this.findTargetItem(e);
2222 if(!t || t.isContainer){
2227 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2228 if(t == this.activeItem && t.shouldDeactivate(e)){
2229 this.activeItem.deactivate();
2230 delete this.activeItem;
2234 this.setActiveItem(t, true);
2242 Roo.log('pass click event');
2246 this.fireEvent("click", this, t, e);
2250 if(!t.href.length || t.href == '#'){
2251 (function() { _this.hide(); }).defer(100);
2256 onMouseOver : function(e){
2257 var t = this.findTargetItem(e);
2260 // if(t.canActivate && !t.disabled){
2261 // this.setActiveItem(t, true);
2265 this.fireEvent("mouseover", this, e, t);
2267 isVisible : function(){
2268 return !this.hidden;
2270 onMouseOut : function(e){
2271 var t = this.findTargetItem(e);
2274 // if(t == this.activeItem && t.shouldDeactivate(e)){
2275 // this.activeItem.deactivate();
2276 // delete this.activeItem;
2279 this.fireEvent("mouseout", this, e, t);
2284 * Displays this menu relative to another element
2285 * @param {String/HTMLElement/Roo.Element} element The element to align to
2286 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287 * the element (defaults to this.defaultAlign)
2288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290 show : function(el, pos, parentMenu){
2291 this.parentMenu = parentMenu;
2295 this.fireEvent("beforeshow", this);
2296 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2299 * Displays this menu at a specific xy position
2300 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303 showAt : function(xy, parentMenu, /* private: */_e){
2304 this.parentMenu = parentMenu;
2309 this.fireEvent("beforeshow", this);
2310 //xy = this.el.adjustForConstraints(xy);
2314 this.hideMenuItems();
2315 this.hidden = false;
2316 this.triggerEl.addClass('open');
2317 this.el.addClass('show');
2319 // reassign x when hitting right
2320 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2324 // reassign y when hitting bottom
2325 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2329 // but the list may align on trigger left or trigger top... should it be a properity?
2331 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2336 this.fireEvent("show", this);
2342 this.doFocus.defer(50, this);
2346 doFocus : function(){
2348 this.focusEl.focus();
2353 * Hides this menu and optionally all parent menus
2354 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356 hide : function(deep)
2359 this.hideMenuItems();
2360 if(this.el && this.isVisible()){
2361 this.fireEvent("beforehide", this);
2362 if(this.activeItem){
2363 this.activeItem.deactivate();
2364 this.activeItem = null;
2366 this.triggerEl.removeClass('open');;
2367 this.el.removeClass('show');
2369 this.fireEvent("hide", this);
2371 if(deep === true && this.parentMenu){
2372 this.parentMenu.hide(true);
2376 onTriggerClick : function(e)
2378 Roo.log('trigger click');
2380 var target = e.getTarget();
2382 Roo.log(target.nodeName.toLowerCase());
2384 if(target.nodeName.toLowerCase() === 'i'){
2390 onTriggerPress : function(e)
2392 Roo.log('trigger press');
2393 //Roo.log(e.getTarget());
2394 // Roo.log(this.triggerEl.dom);
2396 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397 var pel = Roo.get(e.getTarget());
2398 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399 Roo.log('is treeview or dropdown?');
2403 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2407 if (this.isVisible()) {
2412 this.show(this.triggerEl, false, false);
2415 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2422 hideMenuItems : function()
2424 Roo.log("hide Menu Items");
2428 //$(backdrop).remove()
2429 this.el.select('.open',true).each(function(aa) {
2431 aa.removeClass('open');
2432 //var parent = getParent($(this))
2433 //var relatedTarget = { relatedTarget: this }
2435 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436 //if (e.isDefaultPrevented()) return
2437 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2440 addxtypeChild : function (tree, cntr) {
2441 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443 this.menuitems.add(comp);
2455 this.getEl().dom.innerHTML = '';
2456 this.menuitems.clear();
2470 * @class Roo.bootstrap.MenuItem
2471 * @extends Roo.bootstrap.Component
2472 * Bootstrap MenuItem class
2473 * @cfg {String} html the menu label
2474 * @cfg {String} href the link
2475 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477 * @cfg {Boolean} active used on sidebars to highlight active itesm
2478 * @cfg {String} fa favicon to show on left of menu item.
2479 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2483 * Create a new MenuItem
2484 * @param {Object} config The config object
2488 Roo.bootstrap.MenuItem = function(config){
2489 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494 * The raw click event for the entire grid.
2495 * @param {Roo.bootstrap.MenuItem} this
2496 * @param {Roo.EventObject} e
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2506 preventDefault: false,
2507 isContainer : false,
2511 getAutoCreate : function(){
2513 if(this.isContainer){
2516 cls: 'dropdown-menu-item dropdown-item'
2530 if (this.fa !== false) {
2533 cls : 'fa fa-' + this.fa
2542 cls: 'dropdown-menu-item dropdown-item',
2545 if (this.parent().type == 'treeview') {
2546 cfg.cls = 'treeview-menu';
2549 cfg.cls += ' active';
2554 anc.href = this.href || cfg.cn[0].href ;
2555 ctag.html = this.html || cfg.cn[0].html ;
2559 initEvents: function()
2561 if (this.parent().type == 'treeview') {
2562 this.el.select('a').on('click', this.onClick, this);
2566 this.menu.parentType = this.xtype;
2567 this.menu.triggerEl = this.el;
2568 this.menu = this.addxtype(Roo.apply({}, this.menu));
2572 onClick : function(e)
2574 Roo.log('item on click ');
2576 if(this.preventDefault){
2579 //this.parent().hideMenuItems();
2581 this.fireEvent('click', this, e);
2600 * @class Roo.bootstrap.MenuSeparator
2601 * @extends Roo.bootstrap.Component
2602 * Bootstrap MenuSeparator class
2605 * Create a new MenuItem
2606 * @param {Object} config The config object
2610 Roo.bootstrap.MenuSeparator = function(config){
2611 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2616 getAutoCreate : function(){
2635 * @class Roo.bootstrap.Modal
2636 * @extends Roo.bootstrap.Component
2637 * Bootstrap Modal class
2638 * @cfg {String} title Title of dialog
2639 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2641 * @cfg {Boolean} specificTitle default false
2642 * @cfg {Array} buttons Array of buttons or standard button set..
2643 * @cfg {String} buttonPosition (left|right|center) default right
2644 * @cfg {Boolean} animate default true
2645 * @cfg {Boolean} allow_close default true
2646 * @cfg {Boolean} fitwindow default false
2647 * @cfg {String} size (sm|lg) default empty
2648 * @cfg {Number} max_width set the max width of modal
2652 * Create a new Modal Dialog
2653 * @param {Object} config The config object
2656 Roo.bootstrap.Modal = function(config){
2657 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2662 * The raw btnclick event for the button
2663 * @param {Roo.EventObject} e
2668 * Fire when dialog resize
2669 * @param {Roo.bootstrap.Modal} this
2670 * @param {Roo.EventObject} e
2674 this.buttons = this.buttons || [];
2677 this.tmpl = Roo.factory(this.tmpl);
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2684 title : 'test dialog',
2694 specificTitle: false,
2696 buttonPosition: 'right',
2719 onRender : function(ct, position)
2721 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724 var cfg = Roo.apply({}, this.getAutoCreate());
2727 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729 //if (!cfg.name.length) {
2733 cfg.cls += ' ' + this.cls;
2736 cfg.style = this.style;
2738 this.el = Roo.get(document.body).createChild(cfg, position);
2740 //var type = this.el.dom.type;
2743 if(this.tabIndex !== undefined){
2744 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747 this.dialogEl = this.el.select('.modal-dialog',true).first();
2748 this.bodyEl = this.el.select('.modal-body',true).first();
2749 this.closeEl = this.el.select('.modal-header .close', true).first();
2750 this.headerEl = this.el.select('.modal-header',true).first();
2751 this.titleEl = this.el.select('.modal-title',true).first();
2752 this.footerEl = this.el.select('.modal-footer',true).first();
2754 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756 //this.el.addClass("x-dlg-modal");
2758 if (this.buttons.length) {
2759 Roo.each(this.buttons, function(bb) {
2760 var b = Roo.apply({}, bb);
2761 b.xns = b.xns || Roo.bootstrap;
2762 b.xtype = b.xtype || 'Button';
2763 if (typeof(b.listeners) == 'undefined') {
2764 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2767 var btn = Roo.factory(b);
2769 btn.render(this.el.select('.modal-footer div').first());
2773 // render the children.
2776 if(typeof(this.items) != 'undefined'){
2777 var items = this.items;
2780 for(var i =0;i < items.length;i++) {
2781 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2785 this.items = nitems;
2787 // where are these used - they used to be body/close/footer
2791 //this.el.addClass([this.fieldClass, this.cls]);
2795 getAutoCreate : function()
2799 html : this.html || ''
2804 cls : 'modal-title',
2808 if(this.specificTitle){
2814 if (this.allow_close && Roo.bootstrap.version == 3) {
2824 if (this.allow_close && Roo.bootstrap.version == 4) {
2834 if(this.size.length){
2835 size = 'modal-' + this.size;
2842 cls: "modal-dialog " + size,
2845 cls : "modal-content",
2848 cls : 'modal-header',
2853 cls : 'modal-footer',
2857 cls: 'btn-' + this.buttonPosition
2874 modal.cls += ' fade';
2880 getChildContainer : function() {
2885 getButtonContainer : function() {
2886 return this.el.select('.modal-footer div',true).first();
2889 initEvents : function()
2891 if (this.allow_close) {
2892 this.closeEl.on('click', this.hide, this);
2894 Roo.EventManager.onWindowResize(this.resize, this, true);
2901 this.maskEl.setSize(
2902 Roo.lib.Dom.getViewWidth(true),
2903 Roo.lib.Dom.getViewHeight(true)
2906 if (this.fitwindow) {
2908 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2914 if(this.max_width !== 0) {
2916 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919 this.setSize(w, this.height);
2923 if(this.max_height) {
2924 this.setSize(w,Math.min(
2926 Roo.lib.Dom.getViewportHeight(true) - 60
2932 if(!this.fit_content) {
2933 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2937 this.setSize(w, Math.min(
2939 this.headerEl.getHeight() +
2940 this.footerEl.getHeight() +
2941 this.getChildHeight(this.bodyEl.dom.childNodes),
2942 Roo.lib.Dom.getViewportHeight(true) - 60)
2948 setSize : function(w,h)
2959 if (!this.rendered) {
2963 //this.el.setStyle('display', 'block');
2964 this.el.removeClass('hideing');
2965 this.el.dom.style.display='block';
2967 Roo.get(document.body).addClass('modal-open');
2969 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2972 this.el.addClass('show');
2973 this.el.addClass('in');
2976 this.el.addClass('show');
2977 this.el.addClass('in');
2980 // not sure how we can show data in here..
2982 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985 Roo.get(document.body).addClass("x-body-masked");
2987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2988 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989 this.maskEl.dom.style.display = 'block';
2990 this.maskEl.addClass('show');
2995 this.fireEvent('show', this);
2997 // set zindex here - otherwise it appears to be ignored...
2998 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001 this.items.forEach( function(e) {
3002 e.layout ? e.layout() : false;
3010 if(this.fireEvent("beforehide", this) !== false){
3012 this.maskEl.removeClass('show');
3014 this.maskEl.dom.style.display = '';
3015 Roo.get(document.body).removeClass("x-body-masked");
3016 this.el.removeClass('in');
3017 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019 if(this.animate){ // why
3020 this.el.addClass('hideing');
3021 this.el.removeClass('show');
3023 if (!this.el.hasClass('hideing')) {
3024 return; // it's been shown again...
3027 this.el.dom.style.display='';
3029 Roo.get(document.body).removeClass('modal-open');
3030 this.el.removeClass('hideing');
3034 this.el.removeClass('show');
3035 this.el.dom.style.display='';
3036 Roo.get(document.body).removeClass('modal-open');
3039 this.fireEvent('hide', this);
3042 isVisible : function()
3045 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3049 addButton : function(str, cb)
3053 var b = Roo.apply({}, { html : str } );
3054 b.xns = b.xns || Roo.bootstrap;
3055 b.xtype = b.xtype || 'Button';
3056 if (typeof(b.listeners) == 'undefined') {
3057 b.listeners = { click : cb.createDelegate(this) };
3060 var btn = Roo.factory(b);
3062 btn.render(this.el.select('.modal-footer div').first());
3068 setDefaultButton : function(btn)
3070 //this.el.select('.modal-footer').()
3074 resizeTo: function(w,h)
3078 this.dialogEl.setWidth(w);
3079 if (this.diff === false) {
3080 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083 this.bodyEl.setHeight(h - this.diff);
3085 this.fireEvent('resize', this);
3088 setContentSize : function(w, h)
3092 onButtonClick: function(btn,e)
3095 this.fireEvent('btnclick', btn.name, e);
3098 * Set the title of the Dialog
3099 * @param {String} str new Title
3101 setTitle: function(str) {
3102 this.titleEl.dom.innerHTML = str;
3105 * Set the body of the Dialog
3106 * @param {String} str new Title
3108 setBody: function(str) {
3109 this.bodyEl.dom.innerHTML = str;
3112 * Set the body of the Dialog using the template
3113 * @param {Obj} data - apply this data to the template and replace the body contents.
3115 applyBody: function(obj)
3118 Roo.log("Error - using apply Body without a template");
3121 this.tmpl.overwrite(this.bodyEl, obj);
3124 getChildHeight : function(child_nodes)
3128 child_nodes.length == 0
3133 var child_height = 0;
3135 for(var i = 0; i < child_nodes.length; i++) {
3138 * for modal with tabs...
3139 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141 var layout_childs = child_nodes[i].childNodes;
3143 for(var j = 0; j < layout_childs.length; j++) {
3145 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147 var layout_body_childs = layout_childs[j].childNodes;
3149 for(var k = 0; k < layout_body_childs.length; k++) {
3151 if(layout_body_childs[k].classList.contains('navbar')) {
3152 child_height += layout_body_childs[k].offsetHeight;
3156 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3178 child_height += child_nodes[i].offsetHeight;
3179 // Roo.log(child_nodes[i].offsetHeight);
3182 return child_height;
3188 Roo.apply(Roo.bootstrap.Modal, {
3190 * Button config that displays a single OK button
3199 * Button config that displays Yes and No buttons
3215 * Button config that displays OK and Cancel buttons
3230 * Button config that displays Yes, No and Cancel buttons
3254 * messagebox - can be used as a replace
3258 * @class Roo.MessageBox
3259 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268 // process text value...
3272 // Show a dialog using config options:
3274 title:'Save Changes?',
3275 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276 buttons: Roo.Msg.YESNOCANCEL,
3283 Roo.bootstrap.MessageBox = function(){
3284 var dlg, opt, mask, waitTimer;
3285 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286 var buttons, activeTextEl, bwidth;
3290 var handleButton = function(button){
3292 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3296 var handleHide = function(){
3298 dlg.el.removeClass(opt.cls);
3301 // Roo.TaskMgr.stop(waitTimer);
3302 // waitTimer = null;
3307 var updateButtons = function(b){
3310 buttons["ok"].hide();
3311 buttons["cancel"].hide();
3312 buttons["yes"].hide();
3313 buttons["no"].hide();
3314 //dlg.footer.dom.style.display = 'none';
3317 dlg.footerEl.dom.style.display = '';
3318 for(var k in buttons){
3319 if(typeof buttons[k] != "function"){
3322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323 width += buttons[k].el.getWidth()+15;
3333 var handleEsc = function(d, k, e){
3334 if(opt && opt.closable !== false){
3344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345 * @return {Roo.BasicDialog} The BasicDialog element
3347 getDialog : function(){
3349 dlg = new Roo.bootstrap.Modal( {
3352 //constraintoviewport:false,
3354 //collapsible : false,
3359 //buttonAlign:"center",
3360 closeClick : function(){
3361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364 handleButton("cancel");
3369 dlg.on("hide", handleHide);
3371 //dlg.addKeyListener(27, handleEsc);
3373 this.buttons = buttons;
3374 var bt = this.buttonText;
3375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380 bodyEl = dlg.bodyEl.createChild({
3382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383 '<textarea class="roo-mb-textarea"></textarea>' +
3384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3386 msgEl = bodyEl.dom.firstChild;
3387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388 textboxEl.enableDisplayMode();
3389 textboxEl.addKeyListener([10,13], function(){
3390 if(dlg.isVisible() && opt && opt.buttons){
3393 }else if(opt.buttons.yes){
3394 handleButton("yes");
3398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399 textareaEl.enableDisplayMode();
3400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401 progressEl.enableDisplayMode();
3403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404 var pf = progressEl.dom.firstChild;
3406 pp = Roo.get(pf.firstChild);
3407 pp.setHeight(pf.offsetHeight);
3415 * Updates the message box body text
3416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417 * the XHTML-compliant non-breaking space character '&#160;')
3418 * @return {Roo.MessageBox} This message box
3420 updateText : function(text)
3422 if(!dlg.isVisible() && !opt.width){
3423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426 msgEl.innerHTML = text || ' ';
3428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431 Math.min(opt.width || cw , this.maxWidth),
3432 Math.max(opt.minWidth || this.minWidth, bwidth)
3435 activeTextEl.setWidth(w);
3437 if(dlg.isVisible()){
3438 dlg.fixedcenter = false;
3440 // to big, make it scroll. = But as usual stupid IE does not support
3443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447 bodyEl.dom.style.height = '';
3448 bodyEl.dom.style.overflowY = '';
3451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453 bodyEl.dom.style.overflowX = '';
3456 dlg.setContentSize(w, bodyEl.getHeight());
3457 if(dlg.isVisible()){
3458 dlg.fixedcenter = true;
3464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468 * @return {Roo.MessageBox} This message box
3470 updateProgress : function(value, text){
3472 this.updateText(text);
3475 if (pp) { // weird bug on my firefox - for some reason this is not defined
3476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3483 * Returns true if the message box is currently displayed
3484 * @return {Boolean} True if the message box is visible, else false
3486 isVisible : function(){
3487 return dlg && dlg.isVisible();
3491 * Hides the message box if it is displayed
3494 if(this.isVisible()){
3500 * Displays a new message box, or reinitializes an existing message box, based on the config options
3501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502 * The following config object properties are supported:
3504 Property Type Description
3505 ---------- --------------- ------------------------------------------------------------------------------------
3506 animEl String/Element An id or Element from which the message box should animate as it opens and
3507 closes (defaults to undefined)
3508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable Boolean False to hide the top-right close button (defaults to true). Note that
3511 progress and wait dialogs will ignore this property and always hide the
3512 close button as they can only be closed programmatically.
3513 cls String A custom CSS class to apply to the message box element
3514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3515 displayed (defaults to 75)
3516 fn Function A callback function to execute after closing the dialog. The arguments to the
3517 function will be btn (the name of the button that was clicked, if applicable,
3518 e.g. "ok"), and text (the value of the active text field, if applicable).
3519 Progress and wait dialogs will ignore this option since they do not respond to
3520 user actions and can only be closed programmatically, so any required function
3521 should be called by the same code after it closes the dialog.
3522 icon String A CSS class that provides a background image to be used as an icon for
3523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3526 modal Boolean False to allow user interaction with the page while the message box is
3527 displayed (defaults to true)
3528 msg String A string that will replace the existing message box body text (defaults
3529 to the XHTML-compliant non-breaking space character ' ')
3530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3531 progress Boolean True to display a progress bar (defaults to false)
3532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3535 title String The title text
3536 value String The string value to set into the active textbox element if displayed
3537 wait Boolean True to display a progress bar (defaults to false)
3538 width Number The width of the dialog in pixels
3545 msg: 'Please enter your address:',
3547 buttons: Roo.MessageBox.OKCANCEL,
3550 animEl: 'addAddressBtn'
3553 * @param {Object} config Configuration options
3554 * @return {Roo.MessageBox} This message box
3556 show : function(options)
3559 // this causes nightmares if you show one dialog after another
3560 // especially on callbacks..
3562 if(this.isVisible()){
3565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3567 Roo.log("New Dialog Message:" + options.msg )
3568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572 var d = this.getDialog();
3574 d.setTitle(opt.title || " ");
3575 d.closeEl.setDisplayed(opt.closable !== false);
3576 activeTextEl = textboxEl;
3577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3582 textareaEl.setHeight(typeof opt.multiline == "number" ?
3583 opt.multiline : this.defaultTextHeight);
3584 activeTextEl = textareaEl;
3593 progressEl.setDisplayed(opt.progress === true);
3594 this.updateProgress(0);
3595 activeTextEl.dom.value = opt.value || "";
3597 dlg.setDefaultButton(activeTextEl);
3599 var bs = opt.buttons;
3603 }else if(bs && bs.yes){
3604 db = buttons["yes"];
3606 dlg.setDefaultButton(db);
3608 bwidth = updateButtons(opt.buttons);
3609 this.updateText(opt.msg);
3611 d.el.addClass(opt.cls);
3613 d.proxyDrag = opt.proxyDrag === true;
3614 d.modal = opt.modal !== false;
3615 d.mask = opt.modal !== false ? mask : false;
3617 // force it to the end of the z-index stack so it gets a cursor in FF
3618 document.body.appendChild(dlg.el.dom);
3619 d.animateTarget = null;
3620 d.show(options.animEl);
3626 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3627 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628 * and closing the message box when the process is complete.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @return {Roo.MessageBox} This message box
3633 progress : function(title, msg){
3640 minWidth: this.minProgressWidth,
3647 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648 * If a callback function is passed it will be called after the user clicks the button, and the
3649 * id of the button that was clicked will be passed as the only parameter to the callback
3650 * (could also be the top-right close button).
3651 * @param {String} title The title bar text
3652 * @param {String} msg The message box body text
3653 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654 * @param {Object} scope (optional) The scope of the callback function
3655 * @return {Roo.MessageBox} This message box
3657 alert : function(title, msg, fn, scope)
3672 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3673 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674 * You are responsible for closing the message box when the process is complete.
3675 * @param {String} msg The message box body text
3676 * @param {String} title (optional) The title bar text
3677 * @return {Roo.MessageBox} This message box
3679 wait : function(msg, title){
3690 waitTimer = Roo.TaskMgr.start({
3692 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3700 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703 * @param {String} title The title bar text
3704 * @param {String} msg The message box body text
3705 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706 * @param {Object} scope (optional) The scope of the callback function
3707 * @return {Roo.MessageBox} This message box
3709 confirm : function(title, msg, fn, scope){
3713 buttons: this.YESNO,
3722 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3724 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725 * (could also be the top-right close button) and the text that was entered will be passed as the two
3726 * parameters to the callback.
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733 * @return {Roo.MessageBox} This message box
3735 prompt : function(title, msg, fn, scope, multiline){
3739 buttons: this.OKCANCEL,
3744 multiline: multiline,
3751 * Button config that displays a single OK button
3756 * Button config that displays Yes and No buttons
3759 YESNO : {yes:true, no:true},
3761 * Button config that displays OK and Cancel buttons
3764 OKCANCEL : {ok:true, cancel:true},
3766 * Button config that displays Yes, No and Cancel buttons
3769 YESNOCANCEL : {yes:true, no:true, cancel:true},
3772 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775 defaultTextHeight : 75,
3777 * The maximum width in pixels of the message box (defaults to 600)
3782 * The minimum width in pixels of the message box (defaults to 100)
3787 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3788 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791 minProgressWidth : 250,
3793 * An object containing the default button text strings that can be overriden for localized language support.
3794 * Supported properties are: ok, cancel, yes and no.
3795 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3808 * Shorthand for {@link Roo.MessageBox}
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3820 * @class Roo.bootstrap.Navbar
3821 * @extends Roo.bootstrap.Component
3822 * Bootstrap Navbar class
3825 * Create a new Navbar
3826 * @param {Object} config The config object
3830 Roo.bootstrap.Navbar = function(config){
3831 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3835 * @event beforetoggle
3836 * Fire before toggle the menu
3837 * @param {Roo.EventObject} e
3839 "beforetoggle" : true
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3852 getAutoCreate : function(){
3855 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3859 initEvents :function ()
3861 //Roo.log(this.el.select('.navbar-toggle',true));
3862 this.el.select('.navbar-toggle',true).on('click', function() {
3863 if(this.fireEvent('beforetoggle', this) !== false){
3864 var ce = this.el.select('.navbar-collapse',true).first();
3865 ce.toggleClass('in'); // old...
3866 if (ce.hasClass('collapse')) {
3867 ce.removeClass('collapse');
3868 ce.addClass('collapsing');
3869 (function() { ce.removeClass('collapsing'); }).defer(50);
3871 ce.addClass('collapsing');
3873 ce.removeClass('collapsing');
3874 ce.addClass('collapse');
3887 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889 var size = this.el.getSize();
3890 this.maskEl.setSize(size.width, size.height);
3891 this.maskEl.enableDisplayMode("block");
3900 getChildContainer : function()
3902 if (this.el.select('.collapse').getCount()) {
3903 return this.el.select('.collapse',true).first();
3936 * @class Roo.bootstrap.NavSimplebar
3937 * @extends Roo.bootstrap.Navbar
3938 * Bootstrap Sidebar class
3940 * @cfg {Boolean} inverse is inverted color
3942 * @cfg {String} type (nav | pills | tabs)
3943 * @cfg {Boolean} arrangement stacked | justified
3944 * @cfg {String} align (left | right) alignment
3946 * @cfg {Boolean} main (true|false) main nav bar? default false
3947 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3949 * @cfg {String} tag (header|footer|nav|div) default is nav
3951 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3955 * Create a new Sidebar
3956 * @param {Object} config The config object
3960 Roo.bootstrap.NavSimplebar = function(config){
3961 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3964 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3980 getAutoCreate : function(){
3984 tag : this.tag || 'div',
3985 cls : 'navbar navbar-expand-lg'
3987 if (['light','white'].indexOf(this.weight) > -1) {
3988 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3990 cfg.cls += ' bg-' + this.weight;
4002 this.type = this.type || 'nav';
4003 if (['tabs','pills'].indexOf(this.type)!==-1) {
4004 cfg.cn[0].cls += ' nav-' + this.type
4008 if (this.type!=='nav') {
4009 Roo.log('nav type must be nav/tabs/pills')
4011 cfg.cn[0].cls += ' navbar-nav'
4017 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4018 cfg.cn[0].cls += ' nav-' + this.arrangement;
4022 if (this.align === 'right') {
4023 cfg.cn[0].cls += ' navbar-right';
4027 cfg.cls += ' navbar-inverse';
4051 * navbar-expand-md fixed-top
4055 * @class Roo.bootstrap.NavHeaderbar
4056 * @extends Roo.bootstrap.NavSimplebar
4057 * Bootstrap Sidebar class
4059 * @cfg {String} brand what is brand
4060 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4061 * @cfg {String} brand_href href of the brand
4062 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4063 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4064 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4065 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4068 * Create a new Sidebar
4069 * @param {Object} config The config object
4073 Roo.bootstrap.NavHeaderbar = function(config){
4074 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4078 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4085 desktopCenter : false,
4088 getAutoCreate : function(){
4091 tag: this.nav || 'nav',
4092 cls: 'navbar navbar-expand-md',
4098 if (this.desktopCenter) {
4099 cn.push({cls : 'container', cn : []});
4107 cls: 'navbar-toggle navbar-toggler',
4108 'data-toggle': 'collapse',
4113 html: 'Toggle navigation'
4117 cls: 'icon-bar navbar-toggler-icon'
4130 cn.push( Roo.bootstrap.version == 4 ? btn : {
4132 cls: 'navbar-header',
4141 cls: 'collapse navbar-collapse',
4145 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4147 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4148 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4150 // tag can override this..
4152 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4155 if (this.brand !== '') {
4156 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4157 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4159 href: this.brand_href ? this.brand_href : '#',
4160 cls: 'navbar-brand',
4168 cfg.cls += ' main-nav';
4176 getHeaderChildContainer : function()
4178 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4179 return this.el.select('.navbar-header',true).first();
4182 return this.getChildContainer();
4186 initEvents : function()
4188 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4190 if (this.autohide) {
4195 Roo.get(document).on('scroll',function(e) {
4196 var ns = Roo.get(document).getScroll().top;
4197 var os = prevScroll;
4201 ft.removeClass('slideDown');
4202 ft.addClass('slideUp');
4205 ft.removeClass('slideUp');
4206 ft.addClass('slideDown');
4227 * @class Roo.bootstrap.NavSidebar
4228 * @extends Roo.bootstrap.Navbar
4229 * Bootstrap Sidebar class
4232 * Create a new Sidebar
4233 * @param {Object} config The config object
4237 Roo.bootstrap.NavSidebar = function(config){
4238 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4241 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4243 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4245 getAutoCreate : function(){
4250 cls: 'sidebar sidebar-nav'
4272 * @class Roo.bootstrap.NavGroup
4273 * @extends Roo.bootstrap.Component
4274 * Bootstrap NavGroup class
4275 * @cfg {String} align (left|right)
4276 * @cfg {Boolean} inverse
4277 * @cfg {String} type (nav|pills|tab) default nav
4278 * @cfg {String} navId - reference Id for navbar.
4282 * Create a new nav group
4283 * @param {Object} config The config object
4286 Roo.bootstrap.NavGroup = function(config){
4287 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4290 Roo.bootstrap.NavGroup.register(this);
4294 * Fires when the active item changes
4295 * @param {Roo.bootstrap.NavGroup} this
4296 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4297 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4304 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4315 getAutoCreate : function()
4317 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4324 if (['tabs','pills'].indexOf(this.type)!==-1) {
4325 cfg.cls += ' nav-' + this.type
4327 if (this.type!=='nav') {
4328 Roo.log('nav type must be nav/tabs/pills')
4330 cfg.cls += ' navbar-nav'
4333 if (this.parent() && this.parent().sidebar) {
4336 cls: 'dashboard-menu sidebar-menu'
4342 if (this.form === true) {
4348 if (this.align === 'right') {
4349 cfg.cls += ' navbar-right ml-md-auto';
4351 cfg.cls += ' navbar-left';
4355 if (this.align === 'right') {
4356 cfg.cls += ' navbar-right ml-md-auto';
4358 cfg.cls += ' mr-auto';
4362 cfg.cls += ' navbar-inverse';
4370 * sets the active Navigation item
4371 * @param {Roo.bootstrap.NavItem} the new current navitem
4373 setActiveItem : function(item)
4376 Roo.each(this.navItems, function(v){
4381 v.setActive(false, true);
4388 item.setActive(true, true);
4389 this.fireEvent('changed', this, item, prev);
4394 * gets the active Navigation item
4395 * @return {Roo.bootstrap.NavItem} the current navitem
4397 getActive : function()
4401 Roo.each(this.navItems, function(v){
4412 indexOfNav : function()
4416 Roo.each(this.navItems, function(v,i){
4427 * adds a Navigation item
4428 * @param {Roo.bootstrap.NavItem} the navitem to add
4430 addItem : function(cfg)
4432 var cn = new Roo.bootstrap.NavItem(cfg);
4434 cn.parentId = this.id;
4435 cn.onRender(this.el, null);
4439 * register a Navigation item
4440 * @param {Roo.bootstrap.NavItem} the navitem to add
4442 register : function(item)
4444 this.navItems.push( item);
4445 item.navId = this.navId;
4450 * clear all the Navigation item
4453 clearAll : function()
4456 this.el.dom.innerHTML = '';
4459 getNavItem: function(tabId)
4462 Roo.each(this.navItems, function(e) {
4463 if (e.tabId == tabId) {
4473 setActiveNext : function()
4475 var i = this.indexOfNav(this.getActive());
4476 if (i > this.navItems.length) {
4479 this.setActiveItem(this.navItems[i+1]);
4481 setActivePrev : function()
4483 var i = this.indexOfNav(this.getActive());
4487 this.setActiveItem(this.navItems[i-1]);
4489 clearWasActive : function(except) {
4490 Roo.each(this.navItems, function(e) {
4491 if (e.tabId != except.tabId && e.was_active) {
4492 e.was_active = false;
4499 getWasActive : function ()
4502 Roo.each(this.navItems, function(e) {
4517 Roo.apply(Roo.bootstrap.NavGroup, {
4521 * register a Navigation Group
4522 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4524 register : function(navgrp)
4526 this.groups[navgrp.navId] = navgrp;
4530 * fetch a Navigation Group based on the navigation ID
4531 * @param {string} the navgroup to add
4532 * @returns {Roo.bootstrap.NavGroup} the navgroup
4534 get: function(navId) {
4535 if (typeof(this.groups[navId]) == 'undefined') {
4537 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4539 return this.groups[navId] ;
4554 * @class Roo.bootstrap.NavItem
4555 * @extends Roo.bootstrap.Component
4556 * Bootstrap Navbar.NavItem class
4557 * @cfg {String} href link to
4558 * @cfg {String} html content of button
4559 * @cfg {String} badge text inside badge
4560 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4561 * @cfg {String} glyphicon DEPRICATED - use fa
4562 * @cfg {String} icon DEPRICATED - use fa
4563 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4564 * @cfg {Boolean} active Is item active
4565 * @cfg {Boolean} disabled Is item disabled
4567 * @cfg {Boolean} preventDefault (true | false) default false
4568 * @cfg {String} tabId the tab that this item activates.
4569 * @cfg {String} tagtype (a|span) render as a href or span?
4570 * @cfg {Boolean} animateRef (true|false) link to element default false
4573 * Create a new Navbar Item
4574 * @param {Object} config The config object
4576 Roo.bootstrap.NavItem = function(config){
4577 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4582 * The raw click event for the entire grid.
4583 * @param {Roo.EventObject} e
4588 * Fires when the active item active state changes
4589 * @param {Roo.bootstrap.NavItem} this
4590 * @param {boolean} state the new state
4596 * Fires when scroll to element
4597 * @param {Roo.bootstrap.NavItem} this
4598 * @param {Object} options
4599 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4616 preventDefault : false,
4623 getAutoCreate : function(){
4632 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4634 if (this.disabled) {
4635 cfg.cls += ' disabled';
4638 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4642 href : this.href || "#",
4643 html: this.html || ''
4646 if (this.tagtype == 'a') {
4647 cfg.cn[0].cls = 'nav-link';
4650 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4653 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4655 if(this.glyphicon) {
4656 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4661 cfg.cn[0].html += " <span class='caret'></span>";
4665 if (this.badge !== '') {
4667 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4675 initEvents: function()
4677 if (typeof (this.menu) != 'undefined') {
4678 this.menu.parentType = this.xtype;
4679 this.menu.triggerEl = this.el;
4680 this.menu = this.addxtype(Roo.apply({}, this.menu));
4683 this.el.select('a',true).on('click', this.onClick, this);
4685 if(this.tagtype == 'span'){
4686 this.el.select('span',true).on('click', this.onClick, this);
4689 // at this point parent should be available..
4690 this.parent().register(this);
4693 onClick : function(e)
4695 if (e.getTarget('.dropdown-menu-item')) {
4696 // did you click on a menu itemm.... - then don't trigger onclick..
4701 this.preventDefault ||
4704 Roo.log("NavItem - prevent Default?");
4708 if (this.disabled) {
4712 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4713 if (tg && tg.transition) {
4714 Roo.log("waiting for the transitionend");
4720 //Roo.log("fire event clicked");
4721 if(this.fireEvent('click', this, e) === false){
4725 if(this.tagtype == 'span'){
4729 //Roo.log(this.href);
4730 var ael = this.el.select('a',true).first();
4733 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4734 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4735 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4736 return; // ignore... - it's a 'hash' to another page.
4738 Roo.log("NavItem - prevent Default?");
4740 this.scrollToElement(e);
4744 var p = this.parent();
4746 if (['tabs','pills'].indexOf(p.type)!==-1) {
4747 if (typeof(p.setActiveItem) !== 'undefined') {
4748 p.setActiveItem(this);
4752 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4753 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4754 // remove the collapsed menu expand...
4755 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4759 isActive: function () {
4762 setActive : function(state, fire, is_was_active)
4764 if (this.active && !state && this.navId) {
4765 this.was_active = true;
4766 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4768 nv.clearWasActive(this);
4772 this.active = state;
4775 this.el.removeClass('active');
4776 } else if (!this.el.hasClass('active')) {
4777 this.el.addClass('active');
4780 this.fireEvent('changed', this, state);
4783 // show a panel if it's registered and related..
4785 if (!this.navId || !this.tabId || !state || is_was_active) {
4789 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4793 var pan = tg.getPanelByName(this.tabId);
4797 // if we can not flip to new panel - go back to old nav highlight..
4798 if (false == tg.showPanel(pan)) {
4799 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4801 var onav = nv.getWasActive();
4803 onav.setActive(true, false, true);
4812 // this should not be here...
4813 setDisabled : function(state)
4815 this.disabled = state;
4817 this.el.removeClass('disabled');
4818 } else if (!this.el.hasClass('disabled')) {
4819 this.el.addClass('disabled');
4825 * Fetch the element to display the tooltip on.
4826 * @return {Roo.Element} defaults to this.el
4828 tooltipEl : function()
4830 return this.el.select('' + this.tagtype + '', true).first();
4833 scrollToElement : function(e)
4835 var c = document.body;
4838 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4840 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4841 c = document.documentElement;
4844 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4850 var o = target.calcOffsetsTo(c);
4857 this.fireEvent('scrollto', this, options, e);
4859 Roo.get(c).scrollTo('top', options.value, true);
4872 * <span> icon </span>
4873 * <span> text </span>
4874 * <span>badge </span>
4878 * @class Roo.bootstrap.NavSidebarItem
4879 * @extends Roo.bootstrap.NavItem
4880 * Bootstrap Navbar.NavSidebarItem class
4881 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4882 * {Boolean} open is the menu open
4883 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4884 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4885 * {String} buttonSize (sm|md|lg)the extra classes for the button
4886 * {Boolean} showArrow show arrow next to the text (default true)
4888 * Create a new Navbar Button
4889 * @param {Object} config The config object
4891 Roo.bootstrap.NavSidebarItem = function(config){
4892 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4897 * The raw click event for the entire grid.
4898 * @param {Roo.EventObject} e
4903 * Fires when the active item active state changes
4904 * @param {Roo.bootstrap.NavSidebarItem} this
4905 * @param {boolean} state the new state
4913 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4915 badgeWeight : 'default',
4921 buttonWeight : 'default',
4927 getAutoCreate : function(){
4932 href : this.href || '#',
4938 if(this.buttonView){
4941 href : this.href || '#',
4942 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4955 cfg.cls += ' active';
4958 if (this.disabled) {
4959 cfg.cls += ' disabled';
4962 cfg.cls += ' open x-open';
4965 if (this.glyphicon || this.icon) {
4966 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4967 a.cn.push({ tag : 'i', cls : c }) ;
4970 if(!this.buttonView){
4973 html : this.html || ''
4980 if (this.badge !== '') {
4981 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4987 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4990 a.cls += ' dropdown-toggle treeview' ;
4996 initEvents : function()
4998 if (typeof (this.menu) != 'undefined') {
4999 this.menu.parentType = this.xtype;
5000 this.menu.triggerEl = this.el;
5001 this.menu = this.addxtype(Roo.apply({}, this.menu));
5004 this.el.on('click', this.onClick, this);
5006 if(this.badge !== ''){
5007 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5012 onClick : function(e)
5019 if(this.preventDefault){
5023 this.fireEvent('click', this);
5026 disable : function()
5028 this.setDisabled(true);
5033 this.setDisabled(false);
5036 setDisabled : function(state)
5038 if(this.disabled == state){
5042 this.disabled = state;
5045 this.el.addClass('disabled');
5049 this.el.removeClass('disabled');
5054 setActive : function(state)
5056 if(this.active == state){
5060 this.active = state;
5063 this.el.addClass('active');
5067 this.el.removeClass('active');
5072 isActive: function ()
5077 setBadge : function(str)
5083 this.badgeEl.dom.innerHTML = str;
5100 * @class Roo.bootstrap.Row
5101 * @extends Roo.bootstrap.Component
5102 * Bootstrap Row class (contains columns...)
5106 * @param {Object} config The config object
5109 Roo.bootstrap.Row = function(config){
5110 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5113 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5134 * @class Roo.bootstrap.Element
5135 * @extends Roo.bootstrap.Component
5136 * Bootstrap Element class
5137 * @cfg {String} html contents of the element
5138 * @cfg {String} tag tag of the element
5139 * @cfg {String} cls class of the element
5140 * @cfg {Boolean} preventDefault (true|false) default false
5141 * @cfg {Boolean} clickable (true|false) default false
5144 * Create a new Element
5145 * @param {Object} config The config object
5148 Roo.bootstrap.Element = function(config){
5149 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5155 * When a element is chick
5156 * @param {Roo.bootstrap.Element} this
5157 * @param {Roo.EventObject} e
5163 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5168 preventDefault: false,
5171 getAutoCreate : function(){
5175 // cls: this.cls, double assign in parent class Component.js :: onRender
5182 initEvents: function()
5184 Roo.bootstrap.Element.superclass.initEvents.call(this);
5187 this.el.on('click', this.onClick, this);
5192 onClick : function(e)
5194 if(this.preventDefault){
5198 this.fireEvent('click', this, e);
5201 getValue : function()
5203 return this.el.dom.innerHTML;
5206 setValue : function(value)
5208 this.el.dom.innerHTML = value;
5223 * @class Roo.bootstrap.Pagination
5224 * @extends Roo.bootstrap.Component
5225 * Bootstrap Pagination class
5226 * @cfg {String} size xs | sm | md | lg
5227 * @cfg {Boolean} inverse false | true
5230 * Create a new Pagination
5231 * @param {Object} config The config object
5234 Roo.bootstrap.Pagination = function(config){
5235 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5238 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5244 getAutoCreate : function(){
5250 cfg.cls += ' inverse';
5256 cfg.cls += " " + this.cls;
5274 * @class Roo.bootstrap.PaginationItem
5275 * @extends Roo.bootstrap.Component
5276 * Bootstrap PaginationItem class
5277 * @cfg {String} html text
5278 * @cfg {String} href the link
5279 * @cfg {Boolean} preventDefault (true | false) default true
5280 * @cfg {Boolean} active (true | false) default false
5281 * @cfg {Boolean} disabled default false
5285 * Create a new PaginationItem
5286 * @param {Object} config The config object
5290 Roo.bootstrap.PaginationItem = function(config){
5291 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5296 * The raw click event for the entire grid.
5297 * @param {Roo.EventObject} e
5303 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5307 preventDefault: true,
5312 getAutoCreate : function(){
5318 href : this.href ? this.href : '#',
5319 html : this.html ? this.html : ''
5329 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5333 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5339 initEvents: function() {
5341 this.el.on('click', this.onClick, this);
5344 onClick : function(e)
5346 Roo.log('PaginationItem on click ');
5347 if(this.preventDefault){
5355 this.fireEvent('click', this, e);
5371 * @class Roo.bootstrap.Slider
5372 * @extends Roo.bootstrap.Component
5373 * Bootstrap Slider class
5376 * Create a new Slider
5377 * @param {Object} config The config object
5380 Roo.bootstrap.Slider = function(config){
5381 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5384 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5386 getAutoCreate : function(){
5390 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5394 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5406 * Ext JS Library 1.1.1
5407 * Copyright(c) 2006-2007, Ext JS, LLC.
5409 * Originally Released Under LGPL - original licence link has changed is not relivant.
5412 * <script type="text/javascript">
5417 * @class Roo.grid.ColumnModel
5418 * @extends Roo.util.Observable
5419 * This is the default implementation of a ColumnModel used by the Grid. It defines
5420 * the columns in the grid.
5423 var colModel = new Roo.grid.ColumnModel([
5424 {header: "Ticker", width: 60, sortable: true, locked: true},
5425 {header: "Company Name", width: 150, sortable: true},
5426 {header: "Market Cap.", width: 100, sortable: true},
5427 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5428 {header: "Employees", width: 100, sortable: true, resizable: false}
5433 * The config options listed for this class are options which may appear in each
5434 * individual column definition.
5435 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5437 * @param {Object} config An Array of column config objects. See this class's
5438 * config objects for details.
5440 Roo.grid.ColumnModel = function(config){
5442 * The config passed into the constructor
5444 this.config = config;
5447 // if no id, create one
5448 // if the column does not have a dataIndex mapping,
5449 // map it to the order it is in the config
5450 for(var i = 0, len = config.length; i < len; i++){
5452 if(typeof c.dataIndex == "undefined"){
5455 if(typeof c.renderer == "string"){
5456 c.renderer = Roo.util.Format[c.renderer];
5458 if(typeof c.id == "undefined"){
5461 if(c.editor && c.editor.xtype){
5462 c.editor = Roo.factory(c.editor, Roo.grid);
5464 if(c.editor && c.editor.isFormField){
5465 c.editor = new Roo.grid.GridEditor(c.editor);
5467 this.lookup[c.id] = c;
5471 * The width of columns which have no width specified (defaults to 100)
5474 this.defaultWidth = 100;
5477 * Default sortable of columns which have no sortable specified (defaults to false)
5480 this.defaultSortable = false;
5484 * @event widthchange
5485 * Fires when the width of a column changes.
5486 * @param {ColumnModel} this
5487 * @param {Number} columnIndex The column index
5488 * @param {Number} newWidth The new width
5490 "widthchange": true,
5492 * @event headerchange
5493 * Fires when the text of a header changes.
5494 * @param {ColumnModel} this
5495 * @param {Number} columnIndex The column index
5496 * @param {Number} newText The new header text
5498 "headerchange": true,
5500 * @event hiddenchange
5501 * Fires when a column is hidden or "unhidden".
5502 * @param {ColumnModel} this
5503 * @param {Number} columnIndex The column index
5504 * @param {Boolean} hidden true if hidden, false otherwise
5506 "hiddenchange": true,
5508 * @event columnmoved
5509 * Fires when a column is moved.
5510 * @param {ColumnModel} this
5511 * @param {Number} oldIndex
5512 * @param {Number} newIndex
5514 "columnmoved" : true,
5516 * @event columlockchange
5517 * Fires when a column's locked state is changed
5518 * @param {ColumnModel} this
5519 * @param {Number} colIndex
5520 * @param {Boolean} locked true if locked
5522 "columnlockchange" : true
5524 Roo.grid.ColumnModel.superclass.constructor.call(this);
5526 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5528 * @cfg {String} header The header text to display in the Grid view.
5531 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5532 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5533 * specified, the column's index is used as an index into the Record's data Array.
5536 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5537 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5540 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5541 * Defaults to the value of the {@link #defaultSortable} property.
5542 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5545 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5548 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5551 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5554 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5557 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5558 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5559 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5560 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5563 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5566 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5569 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5572 * @cfg {String} cursor (Optional)
5575 * @cfg {String} tooltip (Optional)
5578 * @cfg {Number} xs (Optional)
5581 * @cfg {Number} sm (Optional)
5584 * @cfg {Number} md (Optional)
5587 * @cfg {Number} lg (Optional)
5590 * Returns the id of the column at the specified index.
5591 * @param {Number} index The column index
5592 * @return {String} the id
5594 getColumnId : function(index){
5595 return this.config[index].id;
5599 * Returns the column for a specified id.
5600 * @param {String} id The column id
5601 * @return {Object} the column
5603 getColumnById : function(id){
5604 return this.lookup[id];
5609 * Returns the column for a specified dataIndex.
5610 * @param {String} dataIndex The column dataIndex
5611 * @return {Object|Boolean} the column or false if not found
5613 getColumnByDataIndex: function(dataIndex){
5614 var index = this.findColumnIndex(dataIndex);
5615 return index > -1 ? this.config[index] : false;
5619 * Returns the index for a specified column id.
5620 * @param {String} id The column id
5621 * @return {Number} the index, or -1 if not found
5623 getIndexById : function(id){
5624 for(var i = 0, len = this.config.length; i < len; i++){
5625 if(this.config[i].id == id){
5633 * Returns the index for a specified column dataIndex.
5634 * @param {String} dataIndex The column dataIndex
5635 * @return {Number} the index, or -1 if not found
5638 findColumnIndex : function(dataIndex){
5639 for(var i = 0, len = this.config.length; i < len; i++){
5640 if(this.config[i].dataIndex == dataIndex){
5648 moveColumn : function(oldIndex, newIndex){
5649 var c = this.config[oldIndex];
5650 this.config.splice(oldIndex, 1);
5651 this.config.splice(newIndex, 0, c);
5652 this.dataMap = null;
5653 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5656 isLocked : function(colIndex){
5657 return this.config[colIndex].locked === true;
5660 setLocked : function(colIndex, value, suppressEvent){
5661 if(this.isLocked(colIndex) == value){
5664 this.config[colIndex].locked = value;
5666 this.fireEvent("columnlockchange", this, colIndex, value);
5670 getTotalLockedWidth : function(){
5672 for(var i = 0; i < this.config.length; i++){
5673 if(this.isLocked(i) && !this.isHidden(i)){
5674 this.totalWidth += this.getColumnWidth(i);
5680 getLockedCount : function(){
5681 for(var i = 0, len = this.config.length; i < len; i++){
5682 if(!this.isLocked(i)){
5687 return this.config.length;
5691 * Returns the number of columns.
5694 getColumnCount : function(visibleOnly){
5695 if(visibleOnly === true){
5697 for(var i = 0, len = this.config.length; i < len; i++){
5698 if(!this.isHidden(i)){
5704 return this.config.length;
5708 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5709 * @param {Function} fn
5710 * @param {Object} scope (optional)
5711 * @return {Array} result
5713 getColumnsBy : function(fn, scope){
5715 for(var i = 0, len = this.config.length; i < len; i++){
5716 var c = this.config[i];
5717 if(fn.call(scope||this, c, i) === true){
5725 * Returns true if the specified column is sortable.
5726 * @param {Number} col The column index
5729 isSortable : function(col){
5730 if(typeof this.config[col].sortable == "undefined"){
5731 return this.defaultSortable;
5733 return this.config[col].sortable;
5737 * Returns the rendering (formatting) function defined for the column.
5738 * @param {Number} col The column index.
5739 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5741 getRenderer : function(col){
5742 if(!this.config[col].renderer){
5743 return Roo.grid.ColumnModel.defaultRenderer;
5745 return this.config[col].renderer;
5749 * Sets the rendering (formatting) function for a column.
5750 * @param {Number} col The column index
5751 * @param {Function} fn The function to use to process the cell's raw data
5752 * to return HTML markup for the grid view. The render function is called with
5753 * the following parameters:<ul>
5754 * <li>Data value.</li>
5755 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5756 * <li>css A CSS style string to apply to the table cell.</li>
5757 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5758 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5759 * <li>Row index</li>
5760 * <li>Column index</li>
5761 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5763 setRenderer : function(col, fn){
5764 this.config[col].renderer = fn;
5768 * Returns the width for the specified column.
5769 * @param {Number} col The column index
5772 getColumnWidth : function(col){
5773 return this.config[col].width * 1 || this.defaultWidth;
5777 * Sets the width for a column.
5778 * @param {Number} col The column index
5779 * @param {Number} width The new width
5781 setColumnWidth : function(col, width, suppressEvent){
5782 this.config[col].width = width;
5783 this.totalWidth = null;
5785 this.fireEvent("widthchange", this, col, width);
5790 * Returns the total width of all columns.
5791 * @param {Boolean} includeHidden True to include hidden column widths
5794 getTotalWidth : function(includeHidden){
5795 if(!this.totalWidth){
5796 this.totalWidth = 0;
5797 for(var i = 0, len = this.config.length; i < len; i++){
5798 if(includeHidden || !this.isHidden(i)){
5799 this.totalWidth += this.getColumnWidth(i);
5803 return this.totalWidth;
5807 * Returns the header for the specified column.
5808 * @param {Number} col The column index
5811 getColumnHeader : function(col){
5812 return this.config[col].header;
5816 * Sets the header for a column.
5817 * @param {Number} col The column index
5818 * @param {String} header The new header
5820 setColumnHeader : function(col, header){
5821 this.config[col].header = header;
5822 this.fireEvent("headerchange", this, col, header);
5826 * Returns the tooltip for the specified column.
5827 * @param {Number} col The column index
5830 getColumnTooltip : function(col){
5831 return this.config[col].tooltip;
5834 * Sets the tooltip for a column.
5835 * @param {Number} col The column index
5836 * @param {String} tooltip The new tooltip
5838 setColumnTooltip : function(col, tooltip){
5839 this.config[col].tooltip = tooltip;
5843 * Returns the dataIndex for the specified column.
5844 * @param {Number} col The column index
5847 getDataIndex : function(col){
5848 return this.config[col].dataIndex;
5852 * Sets the dataIndex for a column.
5853 * @param {Number} col The column index
5854 * @param {Number} dataIndex The new dataIndex
5856 setDataIndex : function(col, dataIndex){
5857 this.config[col].dataIndex = dataIndex;
5863 * Returns true if the cell is editable.
5864 * @param {Number} colIndex The column index
5865 * @param {Number} rowIndex The row index - this is nto actually used..?
5868 isCellEditable : function(colIndex, rowIndex){
5869 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5873 * Returns the editor defined for the cell/column.
5874 * return false or null to disable editing.
5875 * @param {Number} colIndex The column index
5876 * @param {Number} rowIndex The row index
5879 getCellEditor : function(colIndex, rowIndex){
5880 return this.config[colIndex].editor;
5884 * Sets if a column is editable.
5885 * @param {Number} col The column index
5886 * @param {Boolean} editable True if the column is editable
5888 setEditable : function(col, editable){
5889 this.config[col].editable = editable;
5894 * Returns true if the column is hidden.
5895 * @param {Number} colIndex The column index
5898 isHidden : function(colIndex){
5899 return this.config[colIndex].hidden;
5904 * Returns true if the column width cannot be changed
5906 isFixed : function(colIndex){
5907 return this.config[colIndex].fixed;
5911 * Returns true if the column can be resized
5914 isResizable : function(colIndex){
5915 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5918 * Sets if a column is hidden.
5919 * @param {Number} colIndex The column index
5920 * @param {Boolean} hidden True if the column is hidden
5922 setHidden : function(colIndex, hidden){
5923 this.config[colIndex].hidden = hidden;
5924 this.totalWidth = null;
5925 this.fireEvent("hiddenchange", this, colIndex, hidden);
5929 * Sets the editor for a column.
5930 * @param {Number} col The column index
5931 * @param {Object} editor The editor object
5933 setEditor : function(col, editor){
5934 this.config[col].editor = editor;
5938 Roo.grid.ColumnModel.defaultRenderer = function(value)
5940 if(typeof value == "object") {
5943 if(typeof value == "string" && value.length < 1){
5947 return String.format("{0}", value);
5950 // Alias for backwards compatibility
5951 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5954 * Ext JS Library 1.1.1
5955 * Copyright(c) 2006-2007, Ext JS, LLC.
5957 * Originally Released Under LGPL - original licence link has changed is not relivant.
5960 * <script type="text/javascript">
5964 * @class Roo.LoadMask
5965 * A simple utility class for generically masking elements while loading data. If the element being masked has
5966 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5967 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5968 * element's UpdateManager load indicator and will be destroyed after the initial load.
5970 * Create a new LoadMask
5971 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5972 * @param {Object} config The config object
5974 Roo.LoadMask = function(el, config){
5975 this.el = Roo.get(el);
5976 Roo.apply(this, config);
5978 this.store.on('beforeload', this.onBeforeLoad, this);
5979 this.store.on('load', this.onLoad, this);
5980 this.store.on('loadexception', this.onLoadException, this);
5981 this.removeMask = false;
5983 var um = this.el.getUpdateManager();
5984 um.showLoadIndicator = false; // disable the default indicator
5985 um.on('beforeupdate', this.onBeforeLoad, this);
5986 um.on('update', this.onLoad, this);
5987 um.on('failure', this.onLoad, this);
5988 this.removeMask = true;
5992 Roo.LoadMask.prototype = {
5994 * @cfg {Boolean} removeMask
5995 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5996 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6000 * The text to display in a centered loading message box (defaults to 'Loading...')
6004 * @cfg {String} msgCls
6005 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6007 msgCls : 'x-mask-loading',
6010 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6016 * Disables the mask to prevent it from being displayed
6018 disable : function(){
6019 this.disabled = true;
6023 * Enables the mask so that it can be displayed
6025 enable : function(){
6026 this.disabled = false;
6029 onLoadException : function()
6033 if (typeof(arguments[3]) != 'undefined') {
6034 Roo.MessageBox.alert("Error loading",arguments[3]);
6038 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6039 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6046 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6051 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6055 onBeforeLoad : function(){
6057 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6062 destroy : function(){
6064 this.store.un('beforeload', this.onBeforeLoad, this);
6065 this.store.un('load', this.onLoad, this);
6066 this.store.un('loadexception', this.onLoadException, this);
6068 var um = this.el.getUpdateManager();
6069 um.un('beforeupdate', this.onBeforeLoad, this);
6070 um.un('update', this.onLoad, this);
6071 um.un('failure', this.onLoad, this);
6082 * @class Roo.bootstrap.Table
6083 * @extends Roo.bootstrap.Component
6084 * Bootstrap Table class
6085 * @cfg {String} cls table class
6086 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6087 * @cfg {String} bgcolor Specifies the background color for a table
6088 * @cfg {Number} border Specifies whether the table cells should have borders or not
6089 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6090 * @cfg {Number} cellspacing Specifies the space between cells
6091 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6092 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6093 * @cfg {String} sortable Specifies that the table should be sortable
6094 * @cfg {String} summary Specifies a summary of the content of a table
6095 * @cfg {Number} width Specifies the width of a table
6096 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6098 * @cfg {boolean} striped Should the rows be alternative striped
6099 * @cfg {boolean} bordered Add borders to the table
6100 * @cfg {boolean} hover Add hover highlighting
6101 * @cfg {boolean} condensed Format condensed
6102 * @cfg {boolean} responsive Format condensed
6103 * @cfg {Boolean} loadMask (true|false) default false
6104 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6105 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6106 * @cfg {Boolean} rowSelection (true|false) default false
6107 * @cfg {Boolean} cellSelection (true|false) default false
6108 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6109 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6110 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6111 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6115 * Create a new Table
6116 * @param {Object} config The config object
6119 Roo.bootstrap.Table = function(config){
6120 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6125 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6126 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6127 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6128 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6130 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6132 this.sm.grid = this;
6133 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6134 this.sm = this.selModel;
6135 this.sm.xmodule = this.xmodule || false;
6138 if (this.cm && typeof(this.cm.config) == 'undefined') {
6139 this.colModel = new Roo.grid.ColumnModel(this.cm);
6140 this.cm = this.colModel;
6141 this.cm.xmodule = this.xmodule || false;
6144 this.store= Roo.factory(this.store, Roo.data);
6145 this.ds = this.store;
6146 this.ds.xmodule = this.xmodule || false;
6149 if (this.footer && this.store) {
6150 this.footer.dataSource = this.ds;
6151 this.footer = Roo.factory(this.footer);
6158 * Fires when a cell is clicked
6159 * @param {Roo.bootstrap.Table} this
6160 * @param {Roo.Element} el
6161 * @param {Number} rowIndex
6162 * @param {Number} columnIndex
6163 * @param {Roo.EventObject} e
6167 * @event celldblclick
6168 * Fires when a cell is double clicked
6169 * @param {Roo.bootstrap.Table} this
6170 * @param {Roo.Element} el
6171 * @param {Number} rowIndex
6172 * @param {Number} columnIndex
6173 * @param {Roo.EventObject} e
6175 "celldblclick" : true,
6178 * Fires when a row is clicked
6179 * @param {Roo.bootstrap.Table} this
6180 * @param {Roo.Element} el
6181 * @param {Number} rowIndex
6182 * @param {Roo.EventObject} e
6186 * @event rowdblclick
6187 * Fires when a row is double clicked
6188 * @param {Roo.bootstrap.Table} this
6189 * @param {Roo.Element} el
6190 * @param {Number} rowIndex
6191 * @param {Roo.EventObject} e
6193 "rowdblclick" : true,
6196 * Fires when a mouseover occur
6197 * @param {Roo.bootstrap.Table} this
6198 * @param {Roo.Element} el
6199 * @param {Number} rowIndex
6200 * @param {Number} columnIndex
6201 * @param {Roo.EventObject} e
6206 * Fires when a mouseout occur
6207 * @param {Roo.bootstrap.Table} this
6208 * @param {Roo.Element} el
6209 * @param {Number} rowIndex
6210 * @param {Number} columnIndex
6211 * @param {Roo.EventObject} e
6216 * Fires when a row is rendered, so you can change add a style to it.
6217 * @param {Roo.bootstrap.Table} this
6218 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6222 * @event rowsrendered
6223 * Fires when all the rows have been rendered
6224 * @param {Roo.bootstrap.Table} this
6226 'rowsrendered' : true,
6228 * @event contextmenu
6229 * The raw contextmenu event for the entire grid.
6230 * @param {Roo.EventObject} e
6232 "contextmenu" : true,
6234 * @event rowcontextmenu
6235 * Fires when a row is right clicked
6236 * @param {Roo.bootstrap.Table} this
6237 * @param {Number} rowIndex
6238 * @param {Roo.EventObject} e
6240 "rowcontextmenu" : true,
6242 * @event cellcontextmenu
6243 * Fires when a cell is right clicked
6244 * @param {Roo.bootstrap.Table} this
6245 * @param {Number} rowIndex
6246 * @param {Number} cellIndex
6247 * @param {Roo.EventObject} e
6249 "cellcontextmenu" : true,
6251 * @event headercontextmenu
6252 * Fires when a header is right clicked
6253 * @param {Roo.bootstrap.Table} this
6254 * @param {Number} columnIndex
6255 * @param {Roo.EventObject} e
6257 "headercontextmenu" : true
6261 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6287 rowSelection : false,
6288 cellSelection : false,
6291 // Roo.Element - the tbody
6293 // Roo.Element - thead element
6296 container: false, // used by gridpanel...
6302 auto_hide_footer : false,
6304 getAutoCreate : function()
6306 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6313 if (this.scrollBody) {
6314 cfg.cls += ' table-body-fixed';
6317 cfg.cls += ' table-striped';
6321 cfg.cls += ' table-hover';
6323 if (this.bordered) {
6324 cfg.cls += ' table-bordered';
6326 if (this.condensed) {
6327 cfg.cls += ' table-condensed';
6329 if (this.responsive) {
6330 cfg.cls += ' table-responsive';
6334 cfg.cls+= ' ' +this.cls;
6337 // this lot should be simplifed...
6350 ].forEach(function(k) {
6358 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6361 if(this.store || this.cm){
6362 if(this.headerShow){
6363 cfg.cn.push(this.renderHeader());
6366 cfg.cn.push(this.renderBody());
6368 if(this.footerShow){
6369 cfg.cn.push(this.renderFooter());
6371 // where does this come from?
6372 //cfg.cls+= ' TableGrid';
6375 return { cn : [ cfg ] };
6378 initEvents : function()
6380 if(!this.store || !this.cm){
6383 if (this.selModel) {
6384 this.selModel.initEvents();
6388 //Roo.log('initEvents with ds!!!!');
6390 this.mainBody = this.el.select('tbody', true).first();
6391 this.mainHead = this.el.select('thead', true).first();
6392 this.mainFoot = this.el.select('tfoot', true).first();
6398 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6399 e.on('click', _this.sort, _this);
6402 this.mainBody.on("click", this.onClick, this);
6403 this.mainBody.on("dblclick", this.onDblClick, this);
6405 // why is this done????? = it breaks dialogs??
6406 //this.parent().el.setStyle('position', 'relative');
6410 this.footer.parentId = this.id;
6411 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6414 this.el.select('tfoot tr td').first().addClass('hide');
6419 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6422 this.store.on('load', this.onLoad, this);
6423 this.store.on('beforeload', this.onBeforeLoad, this);
6424 this.store.on('update', this.onUpdate, this);
6425 this.store.on('add', this.onAdd, this);
6426 this.store.on("clear", this.clear, this);
6428 this.el.on("contextmenu", this.onContextMenu, this);
6430 this.mainBody.on('scroll', this.onBodyScroll, this);
6432 this.cm.on("headerchange", this.onHeaderChange, this);
6434 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6438 onContextMenu : function(e, t)
6440 this.processEvent("contextmenu", e);
6443 processEvent : function(name, e)
6445 if (name != 'touchstart' ) {
6446 this.fireEvent(name, e);
6449 var t = e.getTarget();
6451 var cell = Roo.get(t);
6457 if(cell.findParent('tfoot', false, true)){
6461 if(cell.findParent('thead', false, true)){
6463 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6464 cell = Roo.get(t).findParent('th', false, true);
6466 Roo.log("failed to find th in thead?");
6467 Roo.log(e.getTarget());
6472 var cellIndex = cell.dom.cellIndex;
6474 var ename = name == 'touchstart' ? 'click' : name;
6475 this.fireEvent("header" + ename, this, cellIndex, e);
6480 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6481 cell = Roo.get(t).findParent('td', false, true);
6483 Roo.log("failed to find th in tbody?");
6484 Roo.log(e.getTarget());
6489 var row = cell.findParent('tr', false, true);
6490 var cellIndex = cell.dom.cellIndex;
6491 var rowIndex = row.dom.rowIndex - 1;
6495 this.fireEvent("row" + name, this, rowIndex, e);
6499 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6505 onMouseover : function(e, el)
6507 var cell = Roo.get(el);
6513 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6514 cell = cell.findParent('td', false, true);
6517 var row = cell.findParent('tr', false, true);
6518 var cellIndex = cell.dom.cellIndex;
6519 var rowIndex = row.dom.rowIndex - 1; // start from 0
6521 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6525 onMouseout : function(e, el)
6527 var cell = Roo.get(el);
6533 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6534 cell = cell.findParent('td', false, true);
6537 var row = cell.findParent('tr', false, true);
6538 var cellIndex = cell.dom.cellIndex;
6539 var rowIndex = row.dom.rowIndex - 1; // start from 0
6541 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6545 onClick : function(e, el)
6547 var cell = Roo.get(el);
6549 if(!cell || (!this.cellSelection && !this.rowSelection)){
6553 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6554 cell = cell.findParent('td', false, true);
6557 if(!cell || typeof(cell) == 'undefined'){
6561 var row = cell.findParent('tr', false, true);
6563 if(!row || typeof(row) == 'undefined'){
6567 var cellIndex = cell.dom.cellIndex;
6568 var rowIndex = this.getRowIndex(row);
6570 // why??? - should these not be based on SelectionModel?
6571 if(this.cellSelection){
6572 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6575 if(this.rowSelection){
6576 this.fireEvent('rowclick', this, row, rowIndex, e);
6582 onDblClick : function(e,el)
6584 var cell = Roo.get(el);
6586 if(!cell || (!this.cellSelection && !this.rowSelection)){
6590 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6591 cell = cell.findParent('td', false, true);
6594 if(!cell || typeof(cell) == 'undefined'){
6598 var row = cell.findParent('tr', false, true);
6600 if(!row || typeof(row) == 'undefined'){
6604 var cellIndex = cell.dom.cellIndex;
6605 var rowIndex = this.getRowIndex(row);
6607 if(this.cellSelection){
6608 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6611 if(this.rowSelection){
6612 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6616 sort : function(e,el)
6618 var col = Roo.get(el);
6620 if(!col.hasClass('sortable')){
6624 var sort = col.attr('sort');
6627 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6631 this.store.sortInfo = {field : sort, direction : dir};
6634 Roo.log("calling footer first");
6635 this.footer.onClick('first');
6638 this.store.load({ params : { start : 0 } });
6642 renderHeader : function()
6650 this.totalWidth = 0;
6652 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6654 var config = cm.config[i];
6658 cls : 'x-hcol-' + i,
6660 html: cm.getColumnHeader(i)
6665 if(typeof(config.sortable) != 'undefined' && config.sortable){
6667 c.html = '<i class="glyphicon"></i>' + c.html;
6670 if(typeof(config.lgHeader) != 'undefined'){
6671 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6674 if(typeof(config.mdHeader) != 'undefined'){
6675 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6678 if(typeof(config.smHeader) != 'undefined'){
6679 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6682 if(typeof(config.xsHeader) != 'undefined'){
6683 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6690 if(typeof(config.tooltip) != 'undefined'){
6691 c.tooltip = config.tooltip;
6694 if(typeof(config.colspan) != 'undefined'){
6695 c.colspan = config.colspan;
6698 if(typeof(config.hidden) != 'undefined' && config.hidden){
6699 c.style += ' display:none;';
6702 if(typeof(config.dataIndex) != 'undefined'){
6703 c.sort = config.dataIndex;
6708 if(typeof(config.align) != 'undefined' && config.align.length){
6709 c.style += ' text-align:' + config.align + ';';
6712 if(typeof(config.width) != 'undefined'){
6713 c.style += ' width:' + config.width + 'px;';
6714 this.totalWidth += config.width;
6716 this.totalWidth += 100; // assume minimum of 100 per column?
6719 if(typeof(config.cls) != 'undefined'){
6720 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6723 ['xs','sm','md','lg'].map(function(size){
6725 if(typeof(config[size]) == 'undefined'){
6729 if (!config[size]) { // 0 = hidden
6730 c.cls += ' hidden-' + size;
6734 c.cls += ' col-' + size + '-' + config[size];
6744 renderBody : function()
6754 colspan : this.cm.getColumnCount()
6764 renderFooter : function()
6774 colspan : this.cm.getColumnCount()
6788 // Roo.log('ds onload');
6793 var ds = this.store;
6795 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6796 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6797 if (_this.store.sortInfo) {
6799 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6800 e.select('i', true).addClass(['glyphicon-arrow-up']);
6803 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6804 e.select('i', true).addClass(['glyphicon-arrow-down']);
6809 var tbody = this.mainBody;
6811 if(ds.getCount() > 0){
6812 ds.data.each(function(d,rowIndex){
6813 var row = this.renderRow(cm, ds, rowIndex);
6815 tbody.createChild(row);
6819 if(row.cellObjects.length){
6820 Roo.each(row.cellObjects, function(r){
6821 _this.renderCellObject(r);
6828 var tfoot = this.el.select('tfoot', true).first();
6830 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6832 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6834 var total = this.ds.getTotalCount();
6836 if(this.footer.pageSize < total){
6837 this.mainFoot.show();
6841 Roo.each(this.el.select('tbody td', true).elements, function(e){
6842 e.on('mouseover', _this.onMouseover, _this);
6845 Roo.each(this.el.select('tbody td', true).elements, function(e){
6846 e.on('mouseout', _this.onMouseout, _this);
6848 this.fireEvent('rowsrendered', this);
6854 onUpdate : function(ds,record)
6856 this.refreshRow(record);
6860 onRemove : function(ds, record, index, isUpdate){
6861 if(isUpdate !== true){
6862 this.fireEvent("beforerowremoved", this, index, record);
6864 var bt = this.mainBody.dom;
6866 var rows = this.el.select('tbody > tr', true).elements;
6868 if(typeof(rows[index]) != 'undefined'){
6869 bt.removeChild(rows[index].dom);
6872 // if(bt.rows[index]){
6873 // bt.removeChild(bt.rows[index]);
6876 if(isUpdate !== true){
6877 //this.stripeRows(index);
6878 //this.syncRowHeights(index, index);
6880 this.fireEvent("rowremoved", this, index, record);
6884 onAdd : function(ds, records, rowIndex)
6886 //Roo.log('on Add called');
6887 // - note this does not handle multiple adding very well..
6888 var bt = this.mainBody.dom;
6889 for (var i =0 ; i < records.length;i++) {
6890 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6891 //Roo.log(records[i]);
6892 //Roo.log(this.store.getAt(rowIndex+i));
6893 this.insertRow(this.store, rowIndex + i, false);
6900 refreshRow : function(record){
6901 var ds = this.store, index;
6902 if(typeof record == 'number'){
6904 record = ds.getAt(index);
6906 index = ds.indexOf(record);
6908 this.insertRow(ds, index, true);
6910 this.onRemove(ds, record, index+1, true);
6912 //this.syncRowHeights(index, index);
6914 this.fireEvent("rowupdated", this, index, record);
6917 insertRow : function(dm, rowIndex, isUpdate){
6920 this.fireEvent("beforerowsinserted", this, rowIndex);
6922 //var s = this.getScrollState();
6923 var row = this.renderRow(this.cm, this.store, rowIndex);
6924 // insert before rowIndex..
6925 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6929 if(row.cellObjects.length){
6930 Roo.each(row.cellObjects, function(r){
6931 _this.renderCellObject(r);
6936 this.fireEvent("rowsinserted", this, rowIndex);
6937 //this.syncRowHeights(firstRow, lastRow);
6938 //this.stripeRows(firstRow);
6945 getRowDom : function(rowIndex)
6947 var rows = this.el.select('tbody > tr', true).elements;
6949 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6952 // returns the object tree for a tr..
6955 renderRow : function(cm, ds, rowIndex)
6957 var d = ds.getAt(rowIndex);
6961 cls : 'x-row-' + rowIndex,
6965 var cellObjects = [];
6967 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6968 var config = cm.config[i];
6970 var renderer = cm.getRenderer(i);
6974 if(typeof(renderer) !== 'undefined'){
6975 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6977 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6978 // and are rendered into the cells after the row is rendered - using the id for the element.
6980 if(typeof(value) === 'object'){
6990 rowIndex : rowIndex,
6995 this.fireEvent('rowclass', this, rowcfg);
6999 cls : rowcfg.rowClass + ' x-col-' + i,
7001 html: (typeof(value) === 'object') ? '' : value
7008 if(typeof(config.colspan) != 'undefined'){
7009 td.colspan = config.colspan;
7012 if(typeof(config.hidden) != 'undefined' && config.hidden){
7013 td.style += ' display:none;';
7016 if(typeof(config.align) != 'undefined' && config.align.length){
7017 td.style += ' text-align:' + config.align + ';';
7019 if(typeof(config.valign) != 'undefined' && config.valign.length){
7020 td.style += ' vertical-align:' + config.valign + ';';
7023 if(typeof(config.width) != 'undefined'){
7024 td.style += ' width:' + config.width + 'px;';
7027 if(typeof(config.cursor) != 'undefined'){
7028 td.style += ' cursor:' + config.cursor + ';';
7031 if(typeof(config.cls) != 'undefined'){
7032 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7035 ['xs','sm','md','lg'].map(function(size){
7037 if(typeof(config[size]) == 'undefined'){
7041 if (!config[size]) { // 0 = hidden
7042 td.cls += ' hidden-' + size;
7046 td.cls += ' col-' + size + '-' + config[size];
7054 row.cellObjects = cellObjects;
7062 onBeforeLoad : function()
7071 this.el.select('tbody', true).first().dom.innerHTML = '';
7074 * Show or hide a row.
7075 * @param {Number} rowIndex to show or hide
7076 * @param {Boolean} state hide
7078 setRowVisibility : function(rowIndex, state)
7080 var bt = this.mainBody.dom;
7082 var rows = this.el.select('tbody > tr', true).elements;
7084 if(typeof(rows[rowIndex]) == 'undefined'){
7087 rows[rowIndex].dom.style.display = state ? '' : 'none';
7091 getSelectionModel : function(){
7093 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7095 return this.selModel;
7098 * Render the Roo.bootstrap object from renderder
7100 renderCellObject : function(r)
7104 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7106 var t = r.cfg.render(r.container);
7109 Roo.each(r.cfg.cn, function(c){
7111 container: t.getChildContainer(),
7114 _this.renderCellObject(child);
7119 getRowIndex : function(row)
7123 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7134 * Returns the grid's underlying element = used by panel.Grid
7135 * @return {Element} The element
7137 getGridEl : function(){
7141 * Forces a resize - used by panel.Grid
7142 * @return {Element} The element
7144 autoSize : function()
7146 //var ctr = Roo.get(this.container.dom.parentElement);
7147 var ctr = Roo.get(this.el.dom);
7149 var thd = this.getGridEl().select('thead',true).first();
7150 var tbd = this.getGridEl().select('tbody', true).first();
7151 var tfd = this.getGridEl().select('tfoot', true).first();
7153 var cw = ctr.getWidth();
7157 tbd.setSize(ctr.getWidth(),
7158 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7160 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7163 cw = Math.max(cw, this.totalWidth);
7164 this.getGridEl().select('tr',true).setWidth(cw);
7165 // resize 'expandable coloumn?
7167 return; // we doe not have a view in this design..
7170 onBodyScroll: function()
7172 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7174 this.mainHead.setStyle({
7175 'position' : 'relative',
7176 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7182 var scrollHeight = this.mainBody.dom.scrollHeight;
7184 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7186 var height = this.mainBody.getHeight();
7188 if(scrollHeight - height == scrollTop) {
7190 var total = this.ds.getTotalCount();
7192 if(this.footer.cursor + this.footer.pageSize < total){
7194 this.footer.ds.load({
7196 start : this.footer.cursor + this.footer.pageSize,
7197 limit : this.footer.pageSize
7207 onHeaderChange : function()
7209 var header = this.renderHeader();
7210 var table = this.el.select('table', true).first();
7212 this.mainHead.remove();
7213 this.mainHead = table.createChild(header, this.mainBody, false);
7216 onHiddenChange : function(colModel, colIndex, hidden)
7218 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7219 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7221 this.CSS.updateRule(thSelector, "display", "");
7222 this.CSS.updateRule(tdSelector, "display", "");
7225 this.CSS.updateRule(thSelector, "display", "none");
7226 this.CSS.updateRule(tdSelector, "display", "none");
7229 this.onHeaderChange();
7233 setColumnWidth: function(col_index, width)
7235 // width = "md-2 xs-2..."
7236 if(!this.colModel.config[col_index]) {
7240 var w = width.split(" ");
7242 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7244 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7247 for(var j = 0; j < w.length; j++) {
7253 var size_cls = w[j].split("-");
7255 if(!Number.isInteger(size_cls[1] * 1)) {
7259 if(!this.colModel.config[col_index][size_cls[0]]) {
7263 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7267 h_row[0].classList.replace(
7268 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7269 "col-"+size_cls[0]+"-"+size_cls[1]
7272 for(var i = 0; i < rows.length; i++) {
7274 var size_cls = w[j].split("-");
7276 if(!Number.isInteger(size_cls[1] * 1)) {
7280 if(!this.colModel.config[col_index][size_cls[0]]) {
7284 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7288 rows[i].classList.replace(
7289 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7290 "col-"+size_cls[0]+"-"+size_cls[1]
7294 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7309 * @class Roo.bootstrap.TableCell
7310 * @extends Roo.bootstrap.Component
7311 * Bootstrap TableCell class
7312 * @cfg {String} html cell contain text
7313 * @cfg {String} cls cell class
7314 * @cfg {String} tag cell tag (td|th) default td
7315 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7316 * @cfg {String} align Aligns the content in a cell
7317 * @cfg {String} axis Categorizes cells
7318 * @cfg {String} bgcolor Specifies the background color of a cell
7319 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7320 * @cfg {Number} colspan Specifies the number of columns a cell should span
7321 * @cfg {String} headers Specifies one or more header cells a cell is related to
7322 * @cfg {Number} height Sets the height of a cell
7323 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7324 * @cfg {Number} rowspan Sets the number of rows a cell should span
7325 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7326 * @cfg {String} valign Vertical aligns the content in a cell
7327 * @cfg {Number} width Specifies the width of a cell
7330 * Create a new TableCell
7331 * @param {Object} config The config object
7334 Roo.bootstrap.TableCell = function(config){
7335 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7338 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7358 getAutoCreate : function(){
7359 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7379 cfg.align=this.align
7385 cfg.bgcolor=this.bgcolor
7388 cfg.charoff=this.charoff
7391 cfg.colspan=this.colspan
7394 cfg.headers=this.headers
7397 cfg.height=this.height
7400 cfg.nowrap=this.nowrap
7403 cfg.rowspan=this.rowspan
7406 cfg.scope=this.scope
7409 cfg.valign=this.valign
7412 cfg.width=this.width
7431 * @class Roo.bootstrap.TableRow
7432 * @extends Roo.bootstrap.Component
7433 * Bootstrap TableRow class
7434 * @cfg {String} cls row class
7435 * @cfg {String} align Aligns the content in a table row
7436 * @cfg {String} bgcolor Specifies a background color for a table row
7437 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7438 * @cfg {String} valign Vertical aligns the content in a table row
7441 * Create a new TableRow
7442 * @param {Object} config The config object
7445 Roo.bootstrap.TableRow = function(config){
7446 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7449 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7457 getAutoCreate : function(){
7458 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7468 cfg.align = this.align;
7471 cfg.bgcolor = this.bgcolor;
7474 cfg.charoff = this.charoff;
7477 cfg.valign = this.valign;
7495 * @class Roo.bootstrap.TableBody
7496 * @extends Roo.bootstrap.Component
7497 * Bootstrap TableBody class
7498 * @cfg {String} cls element class
7499 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7500 * @cfg {String} align Aligns the content inside the element
7501 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7502 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7505 * Create a new TableBody
7506 * @param {Object} config The config object
7509 Roo.bootstrap.TableBody = function(config){
7510 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7513 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7521 getAutoCreate : function(){
7522 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7536 cfg.align = this.align;
7539 cfg.charoff = this.charoff;
7542 cfg.valign = this.valign;
7549 // initEvents : function()
7556 // this.store = Roo.factory(this.store, Roo.data);
7557 // this.store.on('load', this.onLoad, this);
7559 // this.store.load();
7563 // onLoad: function ()
7565 // this.fireEvent('load', this);
7575 * Ext JS Library 1.1.1
7576 * Copyright(c) 2006-2007, Ext JS, LLC.
7578 * Originally Released Under LGPL - original licence link has changed is not relivant.
7581 * <script type="text/javascript">
7584 // as we use this in bootstrap.
7585 Roo.namespace('Roo.form');
7587 * @class Roo.form.Action
7588 * Internal Class used to handle form actions
7590 * @param {Roo.form.BasicForm} el The form element or its id
7591 * @param {Object} config Configuration options
7596 // define the action interface
7597 Roo.form.Action = function(form, options){
7599 this.options = options || {};
7602 * Client Validation Failed
7605 Roo.form.Action.CLIENT_INVALID = 'client';
7607 * Server Validation Failed
7610 Roo.form.Action.SERVER_INVALID = 'server';
7612 * Connect to Server Failed
7615 Roo.form.Action.CONNECT_FAILURE = 'connect';
7617 * Reading Data from Server Failed
7620 Roo.form.Action.LOAD_FAILURE = 'load';
7622 Roo.form.Action.prototype = {
7624 failureType : undefined,
7625 response : undefined,
7629 run : function(options){
7634 success : function(response){
7639 handleResponse : function(response){
7643 // default connection failure
7644 failure : function(response){
7646 this.response = response;
7647 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7648 this.form.afterAction(this, false);
7651 processResponse : function(response){
7652 this.response = response;
7653 if(!response.responseText){
7656 this.result = this.handleResponse(response);
7660 // utility functions used internally
7661 getUrl : function(appendParams){
7662 var url = this.options.url || this.form.url || this.form.el.dom.action;
7664 var p = this.getParams();
7666 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7672 getMethod : function(){
7673 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7676 getParams : function(){
7677 var bp = this.form.baseParams;
7678 var p = this.options.params;
7680 if(typeof p == "object"){
7681 p = Roo.urlEncode(Roo.applyIf(p, bp));
7682 }else if(typeof p == 'string' && bp){
7683 p += '&' + Roo.urlEncode(bp);
7686 p = Roo.urlEncode(bp);
7691 createCallback : function(){
7693 success: this.success,
7694 failure: this.failure,
7696 timeout: (this.form.timeout*1000),
7697 upload: this.form.fileUpload ? this.success : undefined
7702 Roo.form.Action.Submit = function(form, options){
7703 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7706 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7709 haveProgress : false,
7710 uploadComplete : false,
7712 // uploadProgress indicator.
7713 uploadProgress : function()
7715 if (!this.form.progressUrl) {
7719 if (!this.haveProgress) {
7720 Roo.MessageBox.progress("Uploading", "Uploading");
7722 if (this.uploadComplete) {
7723 Roo.MessageBox.hide();
7727 this.haveProgress = true;
7729 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7731 var c = new Roo.data.Connection();
7733 url : this.form.progressUrl,
7738 success : function(req){
7739 //console.log(data);
7743 rdata = Roo.decode(req.responseText)
7745 Roo.log("Invalid data from server..");
7749 if (!rdata || !rdata.success) {
7751 Roo.MessageBox.alert(Roo.encode(rdata));
7754 var data = rdata.data;
7756 if (this.uploadComplete) {
7757 Roo.MessageBox.hide();
7762 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7763 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7766 this.uploadProgress.defer(2000,this);
7769 failure: function(data) {
7770 Roo.log('progress url failed ');
7781 // run get Values on the form, so it syncs any secondary forms.
7782 this.form.getValues();
7784 var o = this.options;
7785 var method = this.getMethod();
7786 var isPost = method == 'POST';
7787 if(o.clientValidation === false || this.form.isValid()){
7789 if (this.form.progressUrl) {
7790 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7791 (new Date() * 1) + '' + Math.random());
7796 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7797 form:this.form.el.dom,
7798 url:this.getUrl(!isPost),
7800 params:isPost ? this.getParams() : null,
7801 isUpload: this.form.fileUpload
7804 this.uploadProgress();
7806 }else if (o.clientValidation !== false){ // client validation failed
7807 this.failureType = Roo.form.Action.CLIENT_INVALID;
7808 this.form.afterAction(this, false);
7812 success : function(response)
7814 this.uploadComplete= true;
7815 if (this.haveProgress) {
7816 Roo.MessageBox.hide();
7820 var result = this.processResponse(response);
7821 if(result === true || result.success){
7822 this.form.afterAction(this, true);
7826 this.form.markInvalid(result.errors);
7827 this.failureType = Roo.form.Action.SERVER_INVALID;
7829 this.form.afterAction(this, false);
7831 failure : function(response)
7833 this.uploadComplete= true;
7834 if (this.haveProgress) {
7835 Roo.MessageBox.hide();
7838 this.response = response;
7839 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7840 this.form.afterAction(this, false);
7843 handleResponse : function(response){
7844 if(this.form.errorReader){
7845 var rs = this.form.errorReader.read(response);
7848 for(var i = 0, len = rs.records.length; i < len; i++) {
7849 var r = rs.records[i];
7853 if(errors.length < 1){
7857 success : rs.success,
7863 ret = Roo.decode(response.responseText);
7867 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7877 Roo.form.Action.Load = function(form, options){
7878 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7879 this.reader = this.form.reader;
7882 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7887 Roo.Ajax.request(Roo.apply(
7888 this.createCallback(), {
7889 method:this.getMethod(),
7890 url:this.getUrl(false),
7891 params:this.getParams()
7895 success : function(response){
7897 var result = this.processResponse(response);
7898 if(result === true || !result.success || !result.data){
7899 this.failureType = Roo.form.Action.LOAD_FAILURE;
7900 this.form.afterAction(this, false);
7903 this.form.clearInvalid();
7904 this.form.setValues(result.data);
7905 this.form.afterAction(this, true);
7908 handleResponse : function(response){
7909 if(this.form.reader){
7910 var rs = this.form.reader.read(response);
7911 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7913 success : rs.success,
7917 return Roo.decode(response.responseText);
7921 Roo.form.Action.ACTION_TYPES = {
7922 'load' : Roo.form.Action.Load,
7923 'submit' : Roo.form.Action.Submit
7932 * @class Roo.bootstrap.Form
7933 * @extends Roo.bootstrap.Component
7934 * Bootstrap Form class
7935 * @cfg {String} method GET | POST (default POST)
7936 * @cfg {String} labelAlign top | left (default top)
7937 * @cfg {String} align left | right - for navbars
7938 * @cfg {Boolean} loadMask load mask when submit (default true)
7943 * @param {Object} config The config object
7947 Roo.bootstrap.Form = function(config){
7949 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7951 Roo.bootstrap.Form.popover.apply();
7955 * @event clientvalidation
7956 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7957 * @param {Form} this
7958 * @param {Boolean} valid true if the form has passed client-side validation
7960 clientvalidation: true,
7962 * @event beforeaction
7963 * Fires before any action is performed. Return false to cancel the action.
7964 * @param {Form} this
7965 * @param {Action} action The action to be performed
7969 * @event actionfailed
7970 * Fires when an action fails.
7971 * @param {Form} this
7972 * @param {Action} action The action that failed
7974 actionfailed : true,
7976 * @event actioncomplete
7977 * Fires when an action is completed.
7978 * @param {Form} this
7979 * @param {Action} action The action that completed
7981 actioncomplete : true
7985 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7988 * @cfg {String} method
7989 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7994 * The URL to use for form actions if one isn't supplied in the action options.
7997 * @cfg {Boolean} fileUpload
7998 * Set to true if this form is a file upload.
8002 * @cfg {Object} baseParams
8003 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8007 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8011 * @cfg {Sting} align (left|right) for navbar forms
8016 activeAction : null,
8019 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8020 * element by passing it or its id or mask the form itself by passing in true.
8023 waitMsgTarget : false,
8028 * @cfg {Boolean} errorMask (true|false) default false
8033 * @cfg {Number} maskOffset Default 100
8038 * @cfg {Boolean} maskBody
8042 getAutoCreate : function(){
8046 method : this.method || 'POST',
8047 id : this.id || Roo.id(),
8050 if (this.parent().xtype.match(/^Nav/)) {
8051 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8055 if (this.labelAlign == 'left' ) {
8056 cfg.cls += ' form-horizontal';
8062 initEvents : function()
8064 this.el.on('submit', this.onSubmit, this);
8065 // this was added as random key presses on the form where triggering form submit.
8066 this.el.on('keypress', function(e) {
8067 if (e.getCharCode() != 13) {
8070 // we might need to allow it for textareas.. and some other items.
8071 // check e.getTarget().
8073 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8077 Roo.log("keypress blocked");
8085 onSubmit : function(e){
8090 * Returns true if client-side validation on the form is successful.
8093 isValid : function(){
8094 var items = this.getItems();
8098 items.each(function(f){
8104 Roo.log('invalid field: ' + f.name);
8108 if(!target && f.el.isVisible(true)){
8114 if(this.errorMask && !valid){
8115 Roo.bootstrap.Form.popover.mask(this, target);
8122 * Returns true if any fields in this form have changed since their original load.
8125 isDirty : function(){
8127 var items = this.getItems();
8128 items.each(function(f){
8138 * Performs a predefined action (submit or load) or custom actions you define on this form.
8139 * @param {String} actionName The name of the action type
8140 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8141 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8142 * accept other config options):
8144 Property Type Description
8145 ---------------- --------------- ----------------------------------------------------------------------------------
8146 url String The url for the action (defaults to the form's url)
8147 method String The form method to use (defaults to the form's method, or POST if not defined)
8148 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8149 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8150 validate the form on the client (defaults to false)
8152 * @return {BasicForm} this
8154 doAction : function(action, options){
8155 if(typeof action == 'string'){
8156 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8158 if(this.fireEvent('beforeaction', this, action) !== false){
8159 this.beforeAction(action);
8160 action.run.defer(100, action);
8166 beforeAction : function(action){
8167 var o = action.options;
8172 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8174 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8177 // not really supported yet.. ??
8179 //if(this.waitMsgTarget === true){
8180 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8181 //}else if(this.waitMsgTarget){
8182 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8183 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8185 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8191 afterAction : function(action, success){
8192 this.activeAction = null;
8193 var o = action.options;
8198 Roo.get(document.body).unmask();
8204 //if(this.waitMsgTarget === true){
8205 // this.el.unmask();
8206 //}else if(this.waitMsgTarget){
8207 // this.waitMsgTarget.unmask();
8209 // Roo.MessageBox.updateProgress(1);
8210 // Roo.MessageBox.hide();
8217 Roo.callback(o.success, o.scope, [this, action]);
8218 this.fireEvent('actioncomplete', this, action);
8222 // failure condition..
8223 // we have a scenario where updates need confirming.
8224 // eg. if a locking scenario exists..
8225 // we look for { errors : { needs_confirm : true }} in the response.
8227 (typeof(action.result) != 'undefined') &&
8228 (typeof(action.result.errors) != 'undefined') &&
8229 (typeof(action.result.errors.needs_confirm) != 'undefined')
8232 Roo.log("not supported yet");
8235 Roo.MessageBox.confirm(
8236 "Change requires confirmation",
8237 action.result.errorMsg,
8242 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8252 Roo.callback(o.failure, o.scope, [this, action]);
8253 // show an error message if no failed handler is set..
8254 if (!this.hasListener('actionfailed')) {
8255 Roo.log("need to add dialog support");
8257 Roo.MessageBox.alert("Error",
8258 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8259 action.result.errorMsg :
8260 "Saving Failed, please check your entries or try again"
8265 this.fireEvent('actionfailed', this, action);
8270 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8271 * @param {String} id The value to search for
8274 findField : function(id){
8275 var items = this.getItems();
8276 var field = items.get(id);
8278 items.each(function(f){
8279 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8286 return field || null;
8289 * Mark fields in this form invalid in bulk.
8290 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8291 * @return {BasicForm} this
8293 markInvalid : function(errors){
8294 if(errors instanceof Array){
8295 for(var i = 0, len = errors.length; i < len; i++){
8296 var fieldError = errors[i];
8297 var f = this.findField(fieldError.id);
8299 f.markInvalid(fieldError.msg);
8305 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8306 field.markInvalid(errors[id]);
8310 //Roo.each(this.childForms || [], function (f) {
8311 // f.markInvalid(errors);
8318 * Set values for fields in this form in bulk.
8319 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8320 * @return {BasicForm} this
8322 setValues : function(values){
8323 if(values instanceof Array){ // array of objects
8324 for(var i = 0, len = values.length; i < len; i++){
8326 var f = this.findField(v.id);
8328 f.setValue(v.value);
8329 if(this.trackResetOnLoad){
8330 f.originalValue = f.getValue();
8334 }else{ // object hash
8337 if(typeof values[id] != 'function' && (field = this.findField(id))){
8339 if (field.setFromData &&
8341 field.displayField &&
8342 // combos' with local stores can
8343 // be queried via setValue()
8344 // to set their value..
8345 (field.store && !field.store.isLocal)
8349 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8350 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8351 field.setFromData(sd);
8353 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8355 field.setFromData(values);
8358 field.setValue(values[id]);
8362 if(this.trackResetOnLoad){
8363 field.originalValue = field.getValue();
8369 //Roo.each(this.childForms || [], function (f) {
8370 // f.setValues(values);
8377 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8378 * they are returned as an array.
8379 * @param {Boolean} asString
8382 getValues : function(asString){
8383 //if (this.childForms) {
8384 // copy values from the child forms
8385 // Roo.each(this.childForms, function (f) {
8386 // this.setValues(f.getValues());
8392 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8393 if(asString === true){
8396 return Roo.urlDecode(fs);
8400 * Returns the fields in this form as an object with key/value pairs.
8401 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8404 getFieldValues : function(with_hidden)
8406 var items = this.getItems();
8408 items.each(function(f){
8414 var v = f.getValue();
8416 if (f.inputType =='radio') {
8417 if (typeof(ret[f.getName()]) == 'undefined') {
8418 ret[f.getName()] = ''; // empty..
8421 if (!f.el.dom.checked) {
8429 if(f.xtype == 'MoneyField'){
8430 ret[f.currencyName] = f.getCurrency();
8433 // not sure if this supported any more..
8434 if ((typeof(v) == 'object') && f.getRawValue) {
8435 v = f.getRawValue() ; // dates..
8437 // combo boxes where name != hiddenName...
8438 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8439 ret[f.name] = f.getRawValue();
8441 ret[f.getName()] = v;
8448 * Clears all invalid messages in this form.
8449 * @return {BasicForm} this
8451 clearInvalid : function(){
8452 var items = this.getItems();
8454 items.each(function(f){
8463 * @return {BasicForm} this
8466 var items = this.getItems();
8467 items.each(function(f){
8471 Roo.each(this.childForms || [], function (f) {
8479 getItems : function()
8481 var r=new Roo.util.MixedCollection(false, function(o){
8482 return o.id || (o.id = Roo.id());
8484 var iter = function(el) {
8491 Roo.each(el.items,function(e) {
8500 hideFields : function(items)
8502 Roo.each(items, function(i){
8504 var f = this.findField(i);
8515 showFields : function(items)
8517 Roo.each(items, function(i){
8519 var f = this.findField(i);
8532 Roo.apply(Roo.bootstrap.Form, {
8559 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8560 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8561 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8562 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8565 this.maskEl.top.enableDisplayMode("block");
8566 this.maskEl.left.enableDisplayMode("block");
8567 this.maskEl.bottom.enableDisplayMode("block");
8568 this.maskEl.right.enableDisplayMode("block");
8570 this.toolTip = new Roo.bootstrap.Tooltip({
8571 cls : 'roo-form-error-popover',
8573 'left' : ['r-l', [-2,0], 'right'],
8574 'right' : ['l-r', [2,0], 'left'],
8575 'bottom' : ['tl-bl', [0,2], 'top'],
8576 'top' : [ 'bl-tl', [0,-2], 'bottom']
8580 this.toolTip.render(Roo.get(document.body));
8582 this.toolTip.el.enableDisplayMode("block");
8584 Roo.get(document.body).on('click', function(){
8588 Roo.get(document.body).on('touchstart', function(){
8592 this.isApplied = true
8595 mask : function(form, target)
8599 this.target = target;
8601 if(!this.form.errorMask || !target.el){
8605 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8607 Roo.log(scrollable);
8609 var ot = this.target.el.calcOffsetsTo(scrollable);
8611 var scrollTo = ot[1] - this.form.maskOffset;
8613 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8615 scrollable.scrollTo('top', scrollTo);
8617 var box = this.target.el.getBox();
8619 var zIndex = Roo.bootstrap.Modal.zIndex++;
8622 this.maskEl.top.setStyle('position', 'absolute');
8623 this.maskEl.top.setStyle('z-index', zIndex);
8624 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8625 this.maskEl.top.setLeft(0);
8626 this.maskEl.top.setTop(0);
8627 this.maskEl.top.show();
8629 this.maskEl.left.setStyle('position', 'absolute');
8630 this.maskEl.left.setStyle('z-index', zIndex);
8631 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8632 this.maskEl.left.setLeft(0);
8633 this.maskEl.left.setTop(box.y - this.padding);
8634 this.maskEl.left.show();
8636 this.maskEl.bottom.setStyle('position', 'absolute');
8637 this.maskEl.bottom.setStyle('z-index', zIndex);
8638 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8639 this.maskEl.bottom.setLeft(0);
8640 this.maskEl.bottom.setTop(box.bottom + this.padding);
8641 this.maskEl.bottom.show();
8643 this.maskEl.right.setStyle('position', 'absolute');
8644 this.maskEl.right.setStyle('z-index', zIndex);
8645 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8646 this.maskEl.right.setLeft(box.right + this.padding);
8647 this.maskEl.right.setTop(box.y - this.padding);
8648 this.maskEl.right.show();
8650 this.toolTip.bindEl = this.target.el;
8652 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8654 var tip = this.target.blankText;
8656 if(this.target.getValue() !== '' ) {
8658 if (this.target.invalidText.length) {
8659 tip = this.target.invalidText;
8660 } else if (this.target.regexText.length){
8661 tip = this.target.regexText;
8665 this.toolTip.show(tip);
8667 this.intervalID = window.setInterval(function() {
8668 Roo.bootstrap.Form.popover.unmask();
8671 window.onwheel = function(){ return false;};
8673 (function(){ this.isMasked = true; }).defer(500, this);
8679 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8683 this.maskEl.top.setStyle('position', 'absolute');
8684 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8685 this.maskEl.top.hide();
8687 this.maskEl.left.setStyle('position', 'absolute');
8688 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8689 this.maskEl.left.hide();
8691 this.maskEl.bottom.setStyle('position', 'absolute');
8692 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8693 this.maskEl.bottom.hide();
8695 this.maskEl.right.setStyle('position', 'absolute');
8696 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8697 this.maskEl.right.hide();
8699 this.toolTip.hide();
8701 this.toolTip.el.hide();
8703 window.onwheel = function(){ return true;};
8705 if(this.intervalID){
8706 window.clearInterval(this.intervalID);
8707 this.intervalID = false;
8710 this.isMasked = false;
8720 * Ext JS Library 1.1.1
8721 * Copyright(c) 2006-2007, Ext JS, LLC.
8723 * Originally Released Under LGPL - original licence link has changed is not relivant.
8726 * <script type="text/javascript">
8729 * @class Roo.form.VTypes
8730 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8733 Roo.form.VTypes = function(){
8734 // closure these in so they are only created once.
8735 var alpha = /^[a-zA-Z_]+$/;
8736 var alphanum = /^[a-zA-Z0-9_]+$/;
8737 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8738 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8740 // All these messages and functions are configurable
8743 * The function used to validate email addresses
8744 * @param {String} value The email address
8746 'email' : function(v){
8747 return email.test(v);
8750 * The error text to display when the email validation function returns false
8753 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8755 * The keystroke filter mask to be applied on email input
8758 'emailMask' : /[a-z0-9_\.\-@]/i,
8761 * The function used to validate URLs
8762 * @param {String} value The URL
8764 'url' : function(v){
8768 * The error text to display when the url validation function returns false
8771 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8774 * The function used to validate alpha values
8775 * @param {String} value The value
8777 'alpha' : function(v){
8778 return alpha.test(v);
8781 * The error text to display when the alpha validation function returns false
8784 'alphaText' : 'This field should only contain letters and _',
8786 * The keystroke filter mask to be applied on alpha input
8789 'alphaMask' : /[a-z_]/i,
8792 * The function used to validate alphanumeric values
8793 * @param {String} value The value
8795 'alphanum' : function(v){
8796 return alphanum.test(v);
8799 * The error text to display when the alphanumeric validation function returns false
8802 'alphanumText' : 'This field should only contain letters, numbers and _',
8804 * The keystroke filter mask to be applied on alphanumeric input
8807 'alphanumMask' : /[a-z0-9_]/i
8817 * @class Roo.bootstrap.Input
8818 * @extends Roo.bootstrap.Component
8819 * Bootstrap Input class
8820 * @cfg {Boolean} disabled is it disabled
8821 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8822 * @cfg {String} name name of the input
8823 * @cfg {string} fieldLabel - the label associated
8824 * @cfg {string} placeholder - placeholder to put in text.
8825 * @cfg {string} before - input group add on before
8826 * @cfg {string} after - input group add on after
8827 * @cfg {string} size - (lg|sm) or leave empty..
8828 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8829 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8830 * @cfg {Number} md colspan out of 12 for computer-sized screens
8831 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8832 * @cfg {string} value default value of the input
8833 * @cfg {Number} labelWidth set the width of label
8834 * @cfg {Number} labellg set the width of label (1-12)
8835 * @cfg {Number} labelmd set the width of label (1-12)
8836 * @cfg {Number} labelsm set the width of label (1-12)
8837 * @cfg {Number} labelxs set the width of label (1-12)
8838 * @cfg {String} labelAlign (top|left)
8839 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8840 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8841 * @cfg {String} indicatorpos (left|right) default left
8842 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8843 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8845 * @cfg {String} align (left|center|right) Default left
8846 * @cfg {Boolean} forceFeedback (true|false) Default false
8849 * Create a new Input
8850 * @param {Object} config The config object
8853 Roo.bootstrap.Input = function(config){
8855 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8860 * Fires when this field receives input focus.
8861 * @param {Roo.form.Field} this
8866 * Fires when this field loses input focus.
8867 * @param {Roo.form.Field} this
8872 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8873 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8874 * @param {Roo.form.Field} this
8875 * @param {Roo.EventObject} e The event object
8880 * Fires just before the field blurs if the field value has changed.
8881 * @param {Roo.form.Field} this
8882 * @param {Mixed} newValue The new value
8883 * @param {Mixed} oldValue The original value
8888 * Fires after the field has been marked as invalid.
8889 * @param {Roo.form.Field} this
8890 * @param {String} msg The validation message
8895 * Fires after the field has been validated with no errors.
8896 * @param {Roo.form.Field} this
8901 * Fires after the key up
8902 * @param {Roo.form.Field} this
8903 * @param {Roo.EventObject} e The event Object
8909 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8911 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8912 automatic validation (defaults to "keyup").
8914 validationEvent : "keyup",
8916 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8918 validateOnBlur : true,
8920 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8922 validationDelay : 250,
8924 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8926 focusClass : "x-form-focus", // not needed???
8930 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8932 invalidClass : "has-warning",
8935 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8937 validClass : "has-success",
8940 * @cfg {Boolean} hasFeedback (true|false) default true
8945 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8947 invalidFeedbackClass : "glyphicon-warning-sign",
8950 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8952 validFeedbackClass : "glyphicon-ok",
8955 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8957 selectOnFocus : false,
8960 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8964 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8969 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8971 disableKeyFilter : false,
8974 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8978 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8982 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8984 blankText : "Please complete this mandatory field",
8987 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8991 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8993 maxLength : Number.MAX_VALUE,
8995 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8997 minLengthText : "The minimum length for this field is {0}",
8999 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9001 maxLengthText : "The maximum length for this field is {0}",
9005 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9006 * If available, this function will be called only after the basic validators all return true, and will be passed the
9007 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9011 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9012 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9013 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9017 * @cfg {String} regexText -- Depricated - use Invalid Text
9022 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9028 autocomplete: false,
9047 formatedValue : false,
9048 forceFeedback : false,
9050 indicatorpos : 'left',
9060 parentLabelAlign : function()
9063 while (parent.parent()) {
9064 parent = parent.parent();
9065 if (typeof(parent.labelAlign) !='undefined') {
9066 return parent.labelAlign;
9073 getAutoCreate : function()
9075 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9081 if(this.inputType != 'hidden'){
9082 cfg.cls = 'form-group' //input-group
9088 type : this.inputType,
9090 cls : 'form-control',
9091 placeholder : this.placeholder || '',
9092 autocomplete : this.autocomplete || 'new-password'
9095 if(this.capture.length){
9096 input.capture = this.capture;
9099 if(this.accept.length){
9100 input.accept = this.accept + "/*";
9104 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9107 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9108 input.maxLength = this.maxLength;
9111 if (this.disabled) {
9112 input.disabled=true;
9115 if (this.readOnly) {
9116 input.readonly=true;
9120 input.name = this.name;
9124 input.cls += ' input-' + this.size;
9128 ['xs','sm','md','lg'].map(function(size){
9129 if (settings[size]) {
9130 cfg.cls += ' col-' + size + '-' + settings[size];
9134 var inputblock = input;
9138 cls: 'glyphicon form-control-feedback'
9141 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9144 cls : 'has-feedback',
9152 if (this.before || this.after) {
9155 cls : 'input-group',
9159 if (this.before && typeof(this.before) == 'string') {
9161 inputblock.cn.push({
9163 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9167 if (this.before && typeof(this.before) == 'object') {
9168 this.before = Roo.factory(this.before);
9170 inputblock.cn.push({
9172 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9173 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9177 inputblock.cn.push(input);
9179 if (this.after && typeof(this.after) == 'string') {
9180 inputblock.cn.push({
9182 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9186 if (this.after && typeof(this.after) == 'object') {
9187 this.after = Roo.factory(this.after);
9189 inputblock.cn.push({
9191 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9192 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9196 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9197 inputblock.cls += ' has-feedback';
9198 inputblock.cn.push(feedback);
9203 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9204 tooltip : 'This field is required'
9206 if (Roo.bootstrap.version == 4) {
9209 style : 'display-none'
9212 if (align ==='left' && this.fieldLabel.length) {
9214 cfg.cls += ' roo-form-group-label-left row';
9221 cls : 'control-label col-form-label',
9222 html : this.fieldLabel
9233 var labelCfg = cfg.cn[1];
9234 var contentCfg = cfg.cn[2];
9236 if(this.indicatorpos == 'right'){
9241 cls : 'control-label col-form-label',
9245 html : this.fieldLabel
9259 labelCfg = cfg.cn[0];
9260 contentCfg = cfg.cn[1];
9264 if(this.labelWidth > 12){
9265 labelCfg.style = "width: " + this.labelWidth + 'px';
9268 if(this.labelWidth < 13 && this.labelmd == 0){
9269 this.labelmd = this.labelWidth;
9272 if(this.labellg > 0){
9273 labelCfg.cls += ' col-lg-' + this.labellg;
9274 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9277 if(this.labelmd > 0){
9278 labelCfg.cls += ' col-md-' + this.labelmd;
9279 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9282 if(this.labelsm > 0){
9283 labelCfg.cls += ' col-sm-' + this.labelsm;
9284 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9287 if(this.labelxs > 0){
9288 labelCfg.cls += ' col-xs-' + this.labelxs;
9289 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9293 } else if ( this.fieldLabel.length) {
9298 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9299 tooltip : 'This field is required'
9303 //cls : 'input-group-addon',
9304 html : this.fieldLabel
9312 if(this.indicatorpos == 'right'){
9317 //cls : 'input-group-addon',
9318 html : this.fieldLabel
9323 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9324 tooltip : 'This field is required'
9344 if (this.parentType === 'Navbar' && this.parent().bar) {
9345 cfg.cls += ' navbar-form';
9348 if (this.parentType === 'NavGroup') {
9349 cfg.cls += ' navbar-form';
9357 * return the real input element.
9359 inputEl: function ()
9361 return this.el.select('input.form-control',true).first();
9364 tooltipEl : function()
9366 return this.inputEl();
9369 indicatorEl : function()
9371 if (Roo.bootstrap.version == 4) {
9372 return false; // not enabled in v4 yet.
9375 var indicator = this.el.select('i.roo-required-indicator',true).first();
9385 setDisabled : function(v)
9387 var i = this.inputEl().dom;
9389 i.removeAttribute('disabled');
9393 i.setAttribute('disabled','true');
9395 initEvents : function()
9398 this.inputEl().on("keydown" , this.fireKey, this);
9399 this.inputEl().on("focus", this.onFocus, this);
9400 this.inputEl().on("blur", this.onBlur, this);
9402 this.inputEl().relayEvent('keyup', this);
9404 this.indicator = this.indicatorEl();
9407 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9410 // reference to original value for reset
9411 this.originalValue = this.getValue();
9412 //Roo.form.TextField.superclass.initEvents.call(this);
9413 if(this.validationEvent == 'keyup'){
9414 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9415 this.inputEl().on('keyup', this.filterValidation, this);
9417 else if(this.validationEvent !== false){
9418 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9421 if(this.selectOnFocus){
9422 this.on("focus", this.preFocus, this);
9425 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9426 this.inputEl().on("keypress", this.filterKeys, this);
9428 this.inputEl().relayEvent('keypress', this);
9431 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9432 this.el.on("click", this.autoSize, this);
9435 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9436 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9439 if (typeof(this.before) == 'object') {
9440 this.before.render(this.el.select('.roo-input-before',true).first());
9442 if (typeof(this.after) == 'object') {
9443 this.after.render(this.el.select('.roo-input-after',true).first());
9446 this.inputEl().on('change', this.onChange, this);
9449 filterValidation : function(e){
9450 if(!e.isNavKeyPress()){
9451 this.validationTask.delay(this.validationDelay);
9455 * Validates the field value
9456 * @return {Boolean} True if the value is valid, else false
9458 validate : function(){
9459 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9460 if(this.disabled || this.validateValue(this.getRawValue())){
9471 * Validates a value according to the field's validation rules and marks the field as invalid
9472 * if the validation fails
9473 * @param {Mixed} value The value to validate
9474 * @return {Boolean} True if the value is valid, else false
9476 validateValue : function(value)
9478 if(this.getVisibilityEl().hasClass('hidden')){
9482 if(value.length < 1) { // if it's blank
9483 if(this.allowBlank){
9489 if(value.length < this.minLength){
9492 if(value.length > this.maxLength){
9496 var vt = Roo.form.VTypes;
9497 if(!vt[this.vtype](value, this)){
9501 if(typeof this.validator == "function"){
9502 var msg = this.validator(value);
9506 if (typeof(msg) == 'string') {
9507 this.invalidText = msg;
9511 if(this.regex && !this.regex.test(value)){
9519 fireKey : function(e){
9520 //Roo.log('field ' + e.getKey());
9521 if(e.isNavKeyPress()){
9522 this.fireEvent("specialkey", this, e);
9525 focus : function (selectText){
9527 this.inputEl().focus();
9528 if(selectText === true){
9529 this.inputEl().dom.select();
9535 onFocus : function(){
9536 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9537 // this.el.addClass(this.focusClass);
9540 this.hasFocus = true;
9541 this.startValue = this.getValue();
9542 this.fireEvent("focus", this);
9546 beforeBlur : Roo.emptyFn,
9550 onBlur : function(){
9552 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9553 //this.el.removeClass(this.focusClass);
9555 this.hasFocus = false;
9556 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9559 var v = this.getValue();
9560 if(String(v) !== String(this.startValue)){
9561 this.fireEvent('change', this, v, this.startValue);
9563 this.fireEvent("blur", this);
9566 onChange : function(e)
9568 var v = this.getValue();
9569 if(String(v) !== String(this.startValue)){
9570 this.fireEvent('change', this, v, this.startValue);
9576 * Resets the current field value to the originally loaded value and clears any validation messages
9579 this.setValue(this.originalValue);
9583 * Returns the name of the field
9584 * @return {Mixed} name The name field
9586 getName: function(){
9590 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9591 * @return {Mixed} value The field value
9593 getValue : function(){
9595 var v = this.inputEl().getValue();
9600 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9601 * @return {Mixed} value The field value
9603 getRawValue : function(){
9604 var v = this.inputEl().getValue();
9610 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9611 * @param {Mixed} value The value to set
9613 setRawValue : function(v){
9614 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9617 selectText : function(start, end){
9618 var v = this.getRawValue();
9620 start = start === undefined ? 0 : start;
9621 end = end === undefined ? v.length : end;
9622 var d = this.inputEl().dom;
9623 if(d.setSelectionRange){
9624 d.setSelectionRange(start, end);
9625 }else if(d.createTextRange){
9626 var range = d.createTextRange();
9627 range.moveStart("character", start);
9628 range.moveEnd("character", v.length-end);
9635 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9636 * @param {Mixed} value The value to set
9638 setValue : function(v){
9641 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9647 processValue : function(value){
9648 if(this.stripCharsRe){
9649 var newValue = value.replace(this.stripCharsRe, '');
9650 if(newValue !== value){
9651 this.setRawValue(newValue);
9658 preFocus : function(){
9660 if(this.selectOnFocus){
9661 this.inputEl().dom.select();
9664 filterKeys : function(e){
9666 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9669 var c = e.getCharCode(), cc = String.fromCharCode(c);
9670 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9673 if(!this.maskRe.test(cc)){
9678 * Clear any invalid styles/messages for this field
9680 clearInvalid : function(){
9682 if(!this.el || this.preventMark){ // not rendered
9687 this.el.removeClass(this.invalidClass);
9689 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9691 var feedback = this.el.select('.form-control-feedback', true).first();
9694 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9700 this.indicator.removeClass('visible');
9701 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9704 this.fireEvent('valid', this);
9708 * Mark this field as valid
9710 markValid : function()
9712 if(!this.el || this.preventMark){ // not rendered...
9716 this.el.removeClass([this.invalidClass, this.validClass]);
9718 var feedback = this.el.select('.form-control-feedback', true).first();
9721 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725 this.indicator.removeClass('visible');
9726 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9733 if(this.allowBlank && !this.getRawValue().length){
9737 this.el.addClass(this.validClass);
9739 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9741 var feedback = this.el.select('.form-control-feedback', true).first();
9744 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9745 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9750 this.fireEvent('valid', this);
9754 * Mark this field as invalid
9755 * @param {String} msg The validation message
9757 markInvalid : function(msg)
9759 if(!this.el || this.preventMark){ // not rendered
9763 this.el.removeClass([this.invalidClass, this.validClass]);
9765 var feedback = this.el.select('.form-control-feedback', true).first();
9768 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9775 if(this.allowBlank && !this.getRawValue().length){
9780 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9781 this.indicator.addClass('visible');
9784 this.el.addClass(this.invalidClass);
9786 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9788 var feedback = this.el.select('.form-control-feedback', true).first();
9791 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9793 if(this.getValue().length || this.forceFeedback){
9794 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9801 this.fireEvent('invalid', this, msg);
9804 SafariOnKeyDown : function(event)
9806 // this is a workaround for a password hang bug on chrome/ webkit.
9807 if (this.inputEl().dom.type != 'password') {
9811 var isSelectAll = false;
9813 if(this.inputEl().dom.selectionEnd > 0){
9814 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9816 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9817 event.preventDefault();
9822 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9824 event.preventDefault();
9825 // this is very hacky as keydown always get's upper case.
9827 var cc = String.fromCharCode(event.getCharCode());
9828 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9832 adjustWidth : function(tag, w){
9833 tag = tag.toLowerCase();
9834 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9835 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9839 if(tag == 'textarea'){
9842 }else if(Roo.isOpera){
9846 if(tag == 'textarea'){
9854 setFieldLabel : function(v)
9860 if(this.indicatorEl()){
9861 var ar = this.el.select('label > span',true);
9863 if (ar.elements.length) {
9864 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9865 this.fieldLabel = v;
9869 var br = this.el.select('label',true);
9871 if(br.elements.length) {
9872 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9873 this.fieldLabel = v;
9877 Roo.log('Cannot Found any of label > span || label in input');
9881 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9882 this.fieldLabel = v;
9897 * @class Roo.bootstrap.TextArea
9898 * @extends Roo.bootstrap.Input
9899 * Bootstrap TextArea class
9900 * @cfg {Number} cols Specifies the visible width of a text area
9901 * @cfg {Number} rows Specifies the visible number of lines in a text area
9902 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9903 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9904 * @cfg {string} html text
9907 * Create a new TextArea
9908 * @param {Object} config The config object
9911 Roo.bootstrap.TextArea = function(config){
9912 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9916 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9926 getAutoCreate : function(){
9928 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9934 if(this.inputType != 'hidden'){
9935 cfg.cls = 'form-group' //input-group
9943 value : this.value || '',
9944 html: this.html || '',
9945 cls : 'form-control',
9946 placeholder : this.placeholder || ''
9950 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9951 input.maxLength = this.maxLength;
9955 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9959 input.cols = this.cols;
9962 if (this.readOnly) {
9963 input.readonly = true;
9967 input.name = this.name;
9971 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9975 ['xs','sm','md','lg'].map(function(size){
9976 if (settings[size]) {
9977 cfg.cls += ' col-' + size + '-' + settings[size];
9981 var inputblock = input;
9983 if(this.hasFeedback && !this.allowBlank){
9987 cls: 'glyphicon form-control-feedback'
9991 cls : 'has-feedback',
10000 if (this.before || this.after) {
10003 cls : 'input-group',
10007 inputblock.cn.push({
10009 cls : 'input-group-addon',
10014 inputblock.cn.push(input);
10016 if(this.hasFeedback && !this.allowBlank){
10017 inputblock.cls += ' has-feedback';
10018 inputblock.cn.push(feedback);
10022 inputblock.cn.push({
10024 cls : 'input-group-addon',
10031 if (align ==='left' && this.fieldLabel.length) {
10036 cls : 'control-label',
10037 html : this.fieldLabel
10048 if(this.labelWidth > 12){
10049 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10052 if(this.labelWidth < 13 && this.labelmd == 0){
10053 this.labelmd = this.labelWidth;
10056 if(this.labellg > 0){
10057 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10058 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10061 if(this.labelmd > 0){
10062 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10063 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10066 if(this.labelsm > 0){
10067 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10068 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10071 if(this.labelxs > 0){
10072 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10073 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10076 } else if ( this.fieldLabel.length) {
10081 //cls : 'input-group-addon',
10082 html : this.fieldLabel
10100 if (this.disabled) {
10101 input.disabled=true;
10108 * return the real textarea element.
10110 inputEl: function ()
10112 return this.el.select('textarea.form-control',true).first();
10116 * Clear any invalid styles/messages for this field
10118 clearInvalid : function()
10121 if(!this.el || this.preventMark){ // not rendered
10125 var label = this.el.select('label', true).first();
10126 var icon = this.el.select('i.fa-star', true).first();
10132 this.el.removeClass(this.invalidClass);
10134 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10136 var feedback = this.el.select('.form-control-feedback', true).first();
10139 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10144 this.fireEvent('valid', this);
10148 * Mark this field as valid
10150 markValid : function()
10152 if(!this.el || this.preventMark){ // not rendered
10156 this.el.removeClass([this.invalidClass, this.validClass]);
10158 var feedback = this.el.select('.form-control-feedback', true).first();
10161 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10164 if(this.disabled || this.allowBlank){
10168 var label = this.el.select('label', true).first();
10169 var icon = this.el.select('i.fa-star', true).first();
10175 this.el.addClass(this.validClass);
10177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10179 var feedback = this.el.select('.form-control-feedback', true).first();
10182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10183 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10188 this.fireEvent('valid', this);
10192 * Mark this field as invalid
10193 * @param {String} msg The validation message
10195 markInvalid : function(msg)
10197 if(!this.el || this.preventMark){ // not rendered
10201 this.el.removeClass([this.invalidClass, this.validClass]);
10203 var feedback = this.el.select('.form-control-feedback', true).first();
10206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10209 if(this.disabled || this.allowBlank){
10213 var label = this.el.select('label', true).first();
10214 var icon = this.el.select('i.fa-star', true).first();
10216 if(!this.getValue().length && label && !icon){
10217 this.el.createChild({
10219 cls : 'text-danger fa fa-lg fa-star',
10220 tooltip : 'This field is required',
10221 style : 'margin-right:5px;'
10225 this.el.addClass(this.invalidClass);
10227 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10229 var feedback = this.el.select('.form-control-feedback', true).first();
10232 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10234 if(this.getValue().length || this.forceFeedback){
10235 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10242 this.fireEvent('invalid', this, msg);
10250 * trigger field - base class for combo..
10255 * @class Roo.bootstrap.TriggerField
10256 * @extends Roo.bootstrap.Input
10257 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10258 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10259 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10260 * for which you can provide a custom implementation. For example:
10262 var trigger = new Roo.bootstrap.TriggerField();
10263 trigger.onTriggerClick = myTriggerFn;
10264 trigger.applyTo('my-field');
10267 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10268 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10269 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10270 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10271 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10274 * Create a new TriggerField.
10275 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10276 * to the base TextField)
10278 Roo.bootstrap.TriggerField = function(config){
10279 this.mimicing = false;
10280 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10283 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10285 * @cfg {String} triggerClass A CSS class to apply to the trigger
10288 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10293 * @cfg {Boolean} removable (true|false) special filter default false
10297 /** @cfg {Boolean} grow @hide */
10298 /** @cfg {Number} growMin @hide */
10299 /** @cfg {Number} growMax @hide */
10305 autoSize: Roo.emptyFn,
10309 deferHeight : true,
10312 actionMode : 'wrap',
10317 getAutoCreate : function(){
10319 var align = this.labelAlign || this.parentLabelAlign();
10324 cls: 'form-group' //input-group
10331 type : this.inputType,
10332 cls : 'form-control',
10333 autocomplete: 'new-password',
10334 placeholder : this.placeholder || ''
10338 input.name = this.name;
10341 input.cls += ' input-' + this.size;
10344 if (this.disabled) {
10345 input.disabled=true;
10348 var inputblock = input;
10350 if(this.hasFeedback && !this.allowBlank){
10354 cls: 'glyphicon form-control-feedback'
10357 if(this.removable && !this.editable && !this.tickable){
10359 cls : 'has-feedback',
10365 cls : 'roo-combo-removable-btn close'
10372 cls : 'has-feedback',
10381 if(this.removable && !this.editable && !this.tickable){
10383 cls : 'roo-removable',
10389 cls : 'roo-combo-removable-btn close'
10396 if (this.before || this.after) {
10399 cls : 'input-group',
10403 inputblock.cn.push({
10405 cls : 'input-group-addon input-group-prepend input-group-text',
10410 inputblock.cn.push(input);
10412 if(this.hasFeedback && !this.allowBlank){
10413 inputblock.cls += ' has-feedback';
10414 inputblock.cn.push(feedback);
10418 inputblock.cn.push({
10420 cls : 'input-group-addon input-group-append input-group-text',
10429 var ibwrap = inputblock;
10434 cls: 'roo-select2-choices',
10438 cls: 'roo-select2-search-field',
10450 cls: 'roo-select2-container input-group',
10455 cls: 'form-hidden-field'
10461 if(!this.multiple && this.showToggleBtn){
10467 if (this.caret != false) {
10470 cls: 'fa fa-' + this.caret
10477 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10482 cls: 'combobox-clear',
10496 combobox.cls += ' roo-select2-container-multi';
10500 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10501 tooltip : 'This field is required'
10503 if (Roo.bootstrap.version == 4) {
10506 style : 'display:none'
10511 if (align ==='left' && this.fieldLabel.length) {
10513 cfg.cls += ' roo-form-group-label-left row';
10520 cls : 'control-label',
10521 html : this.fieldLabel
10533 var labelCfg = cfg.cn[1];
10534 var contentCfg = cfg.cn[2];
10536 if(this.indicatorpos == 'right'){
10541 cls : 'control-label',
10545 html : this.fieldLabel
10559 labelCfg = cfg.cn[0];
10560 contentCfg = cfg.cn[1];
10563 if(this.labelWidth > 12){
10564 labelCfg.style = "width: " + this.labelWidth + 'px';
10567 if(this.labelWidth < 13 && this.labelmd == 0){
10568 this.labelmd = this.labelWidth;
10571 if(this.labellg > 0){
10572 labelCfg.cls += ' col-lg-' + this.labellg;
10573 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10576 if(this.labelmd > 0){
10577 labelCfg.cls += ' col-md-' + this.labelmd;
10578 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10581 if(this.labelsm > 0){
10582 labelCfg.cls += ' col-sm-' + this.labelsm;
10583 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10586 if(this.labelxs > 0){
10587 labelCfg.cls += ' col-xs-' + this.labelxs;
10588 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10591 } else if ( this.fieldLabel.length) {
10592 // Roo.log(" label");
10597 //cls : 'input-group-addon',
10598 html : this.fieldLabel
10606 if(this.indicatorpos == 'right'){
10614 html : this.fieldLabel
10628 // Roo.log(" no label && no align");
10635 ['xs','sm','md','lg'].map(function(size){
10636 if (settings[size]) {
10637 cfg.cls += ' col-' + size + '-' + settings[size];
10648 onResize : function(w, h){
10649 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10650 // if(typeof w == 'number'){
10651 // var x = w - this.trigger.getWidth();
10652 // this.inputEl().setWidth(this.adjustWidth('input', x));
10653 // this.trigger.setStyle('left', x+'px');
10658 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10661 getResizeEl : function(){
10662 return this.inputEl();
10666 getPositionEl : function(){
10667 return this.inputEl();
10671 alignErrorIcon : function(){
10672 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10676 initEvents : function(){
10680 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10681 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10682 if(!this.multiple && this.showToggleBtn){
10683 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10684 if(this.hideTrigger){
10685 this.trigger.setDisplayed(false);
10687 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10691 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10694 if(this.removable && !this.editable && !this.tickable){
10695 var close = this.closeTriggerEl();
10698 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10699 close.on('click', this.removeBtnClick, this, close);
10703 //this.trigger.addClassOnOver('x-form-trigger-over');
10704 //this.trigger.addClassOnClick('x-form-trigger-click');
10707 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10711 closeTriggerEl : function()
10713 var close = this.el.select('.roo-combo-removable-btn', true).first();
10714 return close ? close : false;
10717 removeBtnClick : function(e, h, el)
10719 e.preventDefault();
10721 if(this.fireEvent("remove", this) !== false){
10723 this.fireEvent("afterremove", this)
10727 createList : function()
10729 this.list = Roo.get(document.body).createChild({
10730 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10731 cls: 'typeahead typeahead-long dropdown-menu',
10732 style: 'display:none'
10735 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10740 initTrigger : function(){
10745 onDestroy : function(){
10747 this.trigger.removeAllListeners();
10748 // this.trigger.remove();
10751 // this.wrap.remove();
10753 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10757 onFocus : function(){
10758 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10760 if(!this.mimicing){
10761 this.wrap.addClass('x-trigger-wrap-focus');
10762 this.mimicing = true;
10763 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10764 if(this.monitorTab){
10765 this.el.on("keydown", this.checkTab, this);
10772 checkTab : function(e){
10773 if(e.getKey() == e.TAB){
10774 this.triggerBlur();
10779 onBlur : function(){
10784 mimicBlur : function(e, t){
10786 if(!this.wrap.contains(t) && this.validateBlur()){
10787 this.triggerBlur();
10793 triggerBlur : function(){
10794 this.mimicing = false;
10795 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10796 if(this.monitorTab){
10797 this.el.un("keydown", this.checkTab, this);
10799 //this.wrap.removeClass('x-trigger-wrap-focus');
10800 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10804 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10805 validateBlur : function(e, t){
10810 onDisable : function(){
10811 this.inputEl().dom.disabled = true;
10812 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10814 // this.wrap.addClass('x-item-disabled');
10819 onEnable : function(){
10820 this.inputEl().dom.disabled = false;
10821 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10823 // this.el.removeClass('x-item-disabled');
10828 onShow : function(){
10829 var ae = this.getActionEl();
10832 ae.dom.style.display = '';
10833 ae.dom.style.visibility = 'visible';
10839 onHide : function(){
10840 var ae = this.getActionEl();
10841 ae.dom.style.display = 'none';
10845 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10846 * by an implementing function.
10848 * @param {EventObject} e
10850 onTriggerClick : Roo.emptyFn
10854 * Ext JS Library 1.1.1
10855 * Copyright(c) 2006-2007, Ext JS, LLC.
10857 * Originally Released Under LGPL - original licence link has changed is not relivant.
10860 * <script type="text/javascript">
10865 * @class Roo.data.SortTypes
10867 * Defines the default sorting (casting?) comparison functions used when sorting data.
10869 Roo.data.SortTypes = {
10871 * Default sort that does nothing
10872 * @param {Mixed} s The value being converted
10873 * @return {Mixed} The comparison value
10875 none : function(s){
10880 * The regular expression used to strip tags
10884 stripTagsRE : /<\/?[^>]+>/gi,
10887 * Strips all HTML tags to sort on text only
10888 * @param {Mixed} s The value being converted
10889 * @return {String} The comparison value
10891 asText : function(s){
10892 return String(s).replace(this.stripTagsRE, "");
10896 * Strips all HTML tags to sort on text only - Case insensitive
10897 * @param {Mixed} s The value being converted
10898 * @return {String} The comparison value
10900 asUCText : function(s){
10901 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10905 * Case insensitive string
10906 * @param {Mixed} s The value being converted
10907 * @return {String} The comparison value
10909 asUCString : function(s) {
10910 return String(s).toUpperCase();
10915 * @param {Mixed} s The value being converted
10916 * @return {Number} The comparison value
10918 asDate : function(s) {
10922 if(s instanceof Date){
10923 return s.getTime();
10925 return Date.parse(String(s));
10930 * @param {Mixed} s The value being converted
10931 * @return {Float} The comparison value
10933 asFloat : function(s) {
10934 var val = parseFloat(String(s).replace(/,/g, ""));
10943 * @param {Mixed} s The value being converted
10944 * @return {Number} The comparison value
10946 asInt : function(s) {
10947 var val = parseInt(String(s).replace(/,/g, ""));
10955 * Ext JS Library 1.1.1
10956 * Copyright(c) 2006-2007, Ext JS, LLC.
10958 * Originally Released Under LGPL - original licence link has changed is not relivant.
10961 * <script type="text/javascript">
10965 * @class Roo.data.Record
10966 * Instances of this class encapsulate both record <em>definition</em> information, and record
10967 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10968 * to access Records cached in an {@link Roo.data.Store} object.<br>
10970 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10971 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10974 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10976 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10977 * {@link #create}. The parameters are the same.
10978 * @param {Array} data An associative Array of data values keyed by the field name.
10979 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10980 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10981 * not specified an integer id is generated.
10983 Roo.data.Record = function(data, id){
10984 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10989 * Generate a constructor for a specific record layout.
10990 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10991 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10992 * Each field definition object may contain the following properties: <ul>
10993 * <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,
10994 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10995 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10996 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10997 * is being used, then this is a string containing the javascript expression to reference the data relative to
10998 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10999 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11000 * this may be omitted.</p></li>
11001 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11002 * <ul><li>auto (Default, implies no conversion)</li>
11007 * <li>date</li></ul></p></li>
11008 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11009 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11010 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11011 * by the Reader into an object that will be stored in the Record. It is passed the
11012 * following parameters:<ul>
11013 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11015 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11017 * <br>usage:<br><pre><code>
11018 var TopicRecord = Roo.data.Record.create(
11019 {name: 'title', mapping: 'topic_title'},
11020 {name: 'author', mapping: 'username'},
11021 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11022 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11023 {name: 'lastPoster', mapping: 'user2'},
11024 {name: 'excerpt', mapping: 'post_text'}
11027 var myNewRecord = new TopicRecord({
11028 title: 'Do my job please',
11031 lastPost: new Date(),
11032 lastPoster: 'Animal',
11033 excerpt: 'No way dude!'
11035 myStore.add(myNewRecord);
11040 Roo.data.Record.create = function(o){
11041 var f = function(){
11042 f.superclass.constructor.apply(this, arguments);
11044 Roo.extend(f, Roo.data.Record);
11045 var p = f.prototype;
11046 p.fields = new Roo.util.MixedCollection(false, function(field){
11049 for(var i = 0, len = o.length; i < len; i++){
11050 p.fields.add(new Roo.data.Field(o[i]));
11052 f.getField = function(name){
11053 return p.fields.get(name);
11058 Roo.data.Record.AUTO_ID = 1000;
11059 Roo.data.Record.EDIT = 'edit';
11060 Roo.data.Record.REJECT = 'reject';
11061 Roo.data.Record.COMMIT = 'commit';
11063 Roo.data.Record.prototype = {
11065 * Readonly flag - true if this record has been modified.
11074 join : function(store){
11075 this.store = store;
11079 * Set the named field to the specified value.
11080 * @param {String} name The name of the field to set.
11081 * @param {Object} value The value to set the field to.
11083 set : function(name, value){
11084 if(this.data[name] == value){
11088 if(!this.modified){
11089 this.modified = {};
11091 if(typeof this.modified[name] == 'undefined'){
11092 this.modified[name] = this.data[name];
11094 this.data[name] = value;
11095 if(!this.editing && this.store){
11096 this.store.afterEdit(this);
11101 * Get the value of the named field.
11102 * @param {String} name The name of the field to get the value of.
11103 * @return {Object} The value of the field.
11105 get : function(name){
11106 return this.data[name];
11110 beginEdit : function(){
11111 this.editing = true;
11112 this.modified = {};
11116 cancelEdit : function(){
11117 this.editing = false;
11118 delete this.modified;
11122 endEdit : function(){
11123 this.editing = false;
11124 if(this.dirty && this.store){
11125 this.store.afterEdit(this);
11130 * Usually called by the {@link Roo.data.Store} which owns the Record.
11131 * Rejects all changes made to the Record since either creation, or the last commit operation.
11132 * Modified fields are reverted to their original values.
11134 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11135 * of reject operations.
11137 reject : function(){
11138 var m = this.modified;
11140 if(typeof m[n] != "function"){
11141 this.data[n] = m[n];
11144 this.dirty = false;
11145 delete this.modified;
11146 this.editing = false;
11148 this.store.afterReject(this);
11153 * Usually called by the {@link Roo.data.Store} which owns the Record.
11154 * Commits all changes made to the Record since either creation, or the last commit operation.
11156 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11157 * of commit operations.
11159 commit : function(){
11160 this.dirty = false;
11161 delete this.modified;
11162 this.editing = false;
11164 this.store.afterCommit(this);
11169 hasError : function(){
11170 return this.error != null;
11174 clearError : function(){
11179 * Creates a copy of this record.
11180 * @param {String} id (optional) A new record id if you don't want to use this record's id
11183 copy : function(newId) {
11184 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11188 * Ext JS Library 1.1.1
11189 * Copyright(c) 2006-2007, Ext JS, LLC.
11191 * Originally Released Under LGPL - original licence link has changed is not relivant.
11194 * <script type="text/javascript">
11200 * @class Roo.data.Store
11201 * @extends Roo.util.Observable
11202 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11203 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11205 * 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
11206 * has no knowledge of the format of the data returned by the Proxy.<br>
11208 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11209 * instances from the data object. These records are cached and made available through accessor functions.
11211 * Creates a new Store.
11212 * @param {Object} config A config object containing the objects needed for the Store to access data,
11213 * and read the data into Records.
11215 Roo.data.Store = function(config){
11216 this.data = new Roo.util.MixedCollection(false);
11217 this.data.getKey = function(o){
11220 this.baseParams = {};
11222 this.paramNames = {
11227 "multisort" : "_multisort"
11230 if(config && config.data){
11231 this.inlineData = config.data;
11232 delete config.data;
11235 Roo.apply(this, config);
11237 if(this.reader){ // reader passed
11238 this.reader = Roo.factory(this.reader, Roo.data);
11239 this.reader.xmodule = this.xmodule || false;
11240 if(!this.recordType){
11241 this.recordType = this.reader.recordType;
11243 if(this.reader.onMetaChange){
11244 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11248 if(this.recordType){
11249 this.fields = this.recordType.prototype.fields;
11251 this.modified = [];
11255 * @event datachanged
11256 * Fires when the data cache has changed, and a widget which is using this Store
11257 * as a Record cache should refresh its view.
11258 * @param {Store} this
11260 datachanged : true,
11262 * @event metachange
11263 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11264 * @param {Store} this
11265 * @param {Object} meta The JSON metadata
11270 * Fires when Records have been added to the Store
11271 * @param {Store} this
11272 * @param {Roo.data.Record[]} records The array of Records added
11273 * @param {Number} index The index at which the record(s) were added
11278 * Fires when a Record has been removed from the Store
11279 * @param {Store} this
11280 * @param {Roo.data.Record} record The Record that was removed
11281 * @param {Number} index The index at which the record was removed
11286 * Fires when a Record has been updated
11287 * @param {Store} this
11288 * @param {Roo.data.Record} record The Record that was updated
11289 * @param {String} operation The update operation being performed. Value may be one of:
11291 Roo.data.Record.EDIT
11292 Roo.data.Record.REJECT
11293 Roo.data.Record.COMMIT
11299 * Fires when the data cache has been cleared.
11300 * @param {Store} this
11304 * @event beforeload
11305 * Fires before a request is made for a new data object. If the beforeload handler returns false
11306 * the load action will be canceled.
11307 * @param {Store} this
11308 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11312 * @event beforeloadadd
11313 * Fires after a new set of Records has been loaded.
11314 * @param {Store} this
11315 * @param {Roo.data.Record[]} records The Records that were loaded
11316 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11318 beforeloadadd : true,
11321 * Fires after a new set of Records has been loaded, before they are added to the store.
11322 * @param {Store} this
11323 * @param {Roo.data.Record[]} records The Records that were loaded
11324 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11325 * @params {Object} return from reader
11329 * @event loadexception
11330 * Fires if an exception occurs in the Proxy during loading.
11331 * Called with the signature of the Proxy's "loadexception" event.
11332 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11335 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11336 * @param {Object} load options
11337 * @param {Object} jsonData from your request (normally this contains the Exception)
11339 loadexception : true
11343 this.proxy = Roo.factory(this.proxy, Roo.data);
11344 this.proxy.xmodule = this.xmodule || false;
11345 this.relayEvents(this.proxy, ["loadexception"]);
11347 this.sortToggle = {};
11348 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11350 Roo.data.Store.superclass.constructor.call(this);
11352 if(this.inlineData){
11353 this.loadData(this.inlineData);
11354 delete this.inlineData;
11358 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11360 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11361 * without a remote query - used by combo/forms at present.
11365 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11368 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11371 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11372 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11375 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11376 * on any HTTP request
11379 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11382 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11386 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11387 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11389 remoteSort : false,
11392 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11393 * loaded or when a record is removed. (defaults to false).
11395 pruneModifiedRecords : false,
11398 lastOptions : null,
11401 * Add Records to the Store and fires the add event.
11402 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11404 add : function(records){
11405 records = [].concat(records);
11406 for(var i = 0, len = records.length; i < len; i++){
11407 records[i].join(this);
11409 var index = this.data.length;
11410 this.data.addAll(records);
11411 this.fireEvent("add", this, records, index);
11415 * Remove a Record from the Store and fires the remove event.
11416 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11418 remove : function(record){
11419 var index = this.data.indexOf(record);
11420 this.data.removeAt(index);
11422 if(this.pruneModifiedRecords){
11423 this.modified.remove(record);
11425 this.fireEvent("remove", this, record, index);
11429 * Remove all Records from the Store and fires the clear event.
11431 removeAll : function(){
11433 if(this.pruneModifiedRecords){
11434 this.modified = [];
11436 this.fireEvent("clear", this);
11440 * Inserts Records to the Store at the given index and fires the add event.
11441 * @param {Number} index The start index at which to insert the passed Records.
11442 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11444 insert : function(index, records){
11445 records = [].concat(records);
11446 for(var i = 0, len = records.length; i < len; i++){
11447 this.data.insert(index, records[i]);
11448 records[i].join(this);
11450 this.fireEvent("add", this, records, index);
11454 * Get the index within the cache of the passed Record.
11455 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11456 * @return {Number} The index of the passed Record. Returns -1 if not found.
11458 indexOf : function(record){
11459 return this.data.indexOf(record);
11463 * Get the index within the cache of the Record with the passed id.
11464 * @param {String} id The id of the Record to find.
11465 * @return {Number} The index of the Record. Returns -1 if not found.
11467 indexOfId : function(id){
11468 return this.data.indexOfKey(id);
11472 * Get the Record with the specified id.
11473 * @param {String} id The id of the Record to find.
11474 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11476 getById : function(id){
11477 return this.data.key(id);
11481 * Get the Record at the specified index.
11482 * @param {Number} index The index of the Record to find.
11483 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11485 getAt : function(index){
11486 return this.data.itemAt(index);
11490 * Returns a range of Records between specified indices.
11491 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11492 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11493 * @return {Roo.data.Record[]} An array of Records
11495 getRange : function(start, end){
11496 return this.data.getRange(start, end);
11500 storeOptions : function(o){
11501 o = Roo.apply({}, o);
11504 this.lastOptions = o;
11508 * Loads the Record cache from the configured Proxy using the configured Reader.
11510 * If using remote paging, then the first load call must specify the <em>start</em>
11511 * and <em>limit</em> properties in the options.params property to establish the initial
11512 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11514 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11515 * and this call will return before the new data has been loaded. Perform any post-processing
11516 * in a callback function, or in a "load" event handler.</strong>
11518 * @param {Object} options An object containing properties which control loading options:<ul>
11519 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11520 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11521 * passed the following arguments:<ul>
11522 * <li>r : Roo.data.Record[]</li>
11523 * <li>options: Options object from the load call</li>
11524 * <li>success: Boolean success indicator</li></ul></li>
11525 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11526 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11529 load : function(options){
11530 options = options || {};
11531 if(this.fireEvent("beforeload", this, options) !== false){
11532 this.storeOptions(options);
11533 var p = Roo.apply(options.params || {}, this.baseParams);
11534 // if meta was not loaded from remote source.. try requesting it.
11535 if (!this.reader.metaFromRemote) {
11536 p._requestMeta = 1;
11538 if(this.sortInfo && this.remoteSort){
11539 var pn = this.paramNames;
11540 p[pn["sort"]] = this.sortInfo.field;
11541 p[pn["dir"]] = this.sortInfo.direction;
11543 if (this.multiSort) {
11544 var pn = this.paramNames;
11545 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11548 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11553 * Reloads the Record cache from the configured Proxy using the configured Reader and
11554 * the options from the last load operation performed.
11555 * @param {Object} options (optional) An object containing properties which may override the options
11556 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11557 * the most recently used options are reused).
11559 reload : function(options){
11560 this.load(Roo.applyIf(options||{}, this.lastOptions));
11564 // Called as a callback by the Reader during a load operation.
11565 loadRecords : function(o, options, success){
11566 if(!o || success === false){
11567 if(success !== false){
11568 this.fireEvent("load", this, [], options, o);
11570 if(options.callback){
11571 options.callback.call(options.scope || this, [], options, false);
11575 // if data returned failure - throw an exception.
11576 if (o.success === false) {
11577 // show a message if no listener is registered.
11578 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11579 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11581 // loadmask wil be hooked into this..
11582 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11585 var r = o.records, t = o.totalRecords || r.length;
11587 this.fireEvent("beforeloadadd", this, r, options, o);
11589 if(!options || options.add !== true){
11590 if(this.pruneModifiedRecords){
11591 this.modified = [];
11593 for(var i = 0, len = r.length; i < len; i++){
11597 this.data = this.snapshot;
11598 delete this.snapshot;
11601 this.data.addAll(r);
11602 this.totalLength = t;
11604 this.fireEvent("datachanged", this);
11606 this.totalLength = Math.max(t, this.data.length+r.length);
11610 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11612 var e = new Roo.data.Record({});
11614 e.set(this.parent.displayField, this.parent.emptyTitle);
11615 e.set(this.parent.valueField, '');
11620 this.fireEvent("load", this, r, options, o);
11621 if(options.callback){
11622 options.callback.call(options.scope || this, r, options, true);
11628 * Loads data from a passed data block. A Reader which understands the format of the data
11629 * must have been configured in the constructor.
11630 * @param {Object} data The data block from which to read the Records. The format of the data expected
11631 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11632 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11634 loadData : function(o, append){
11635 var r = this.reader.readRecords(o);
11636 this.loadRecords(r, {add: append}, true);
11640 * Gets the number of cached records.
11642 * <em>If using paging, this may not be the total size of the dataset. If the data object
11643 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11644 * the data set size</em>
11646 getCount : function(){
11647 return this.data.length || 0;
11651 * Gets the total number of records in the dataset as returned by the server.
11653 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11654 * the dataset size</em>
11656 getTotalCount : function(){
11657 return this.totalLength || 0;
11661 * Returns the sort state of the Store as an object with two properties:
11663 field {String} The name of the field by which the Records are sorted
11664 direction {String} The sort order, "ASC" or "DESC"
11667 getSortState : function(){
11668 return this.sortInfo;
11672 applySort : function(){
11673 if(this.sortInfo && !this.remoteSort){
11674 var s = this.sortInfo, f = s.field;
11675 var st = this.fields.get(f).sortType;
11676 var fn = function(r1, r2){
11677 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11678 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11680 this.data.sort(s.direction, fn);
11681 if(this.snapshot && this.snapshot != this.data){
11682 this.snapshot.sort(s.direction, fn);
11688 * Sets the default sort column and order to be used by the next load operation.
11689 * @param {String} fieldName The name of the field to sort by.
11690 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11692 setDefaultSort : function(field, dir){
11693 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11697 * Sort the Records.
11698 * If remote sorting is used, the sort is performed on the server, and the cache is
11699 * reloaded. If local sorting is used, the cache is sorted internally.
11700 * @param {String} fieldName The name of the field to sort by.
11701 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11703 sort : function(fieldName, dir){
11704 var f = this.fields.get(fieldName);
11706 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11708 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11709 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11714 this.sortToggle[f.name] = dir;
11715 this.sortInfo = {field: f.name, direction: dir};
11716 if(!this.remoteSort){
11718 this.fireEvent("datachanged", this);
11720 this.load(this.lastOptions);
11725 * Calls the specified function for each of the Records in the cache.
11726 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11727 * Returning <em>false</em> aborts and exits the iteration.
11728 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11730 each : function(fn, scope){
11731 this.data.each(fn, scope);
11735 * Gets all records modified since the last commit. Modified records are persisted across load operations
11736 * (e.g., during paging).
11737 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11739 getModifiedRecords : function(){
11740 return this.modified;
11744 createFilterFn : function(property, value, anyMatch){
11745 if(!value.exec){ // not a regex
11746 value = String(value);
11747 if(value.length == 0){
11750 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11752 return function(r){
11753 return value.test(r.data[property]);
11758 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11759 * @param {String} property A field on your records
11760 * @param {Number} start The record index to start at (defaults to 0)
11761 * @param {Number} end The last record index to include (defaults to length - 1)
11762 * @return {Number} The sum
11764 sum : function(property, start, end){
11765 var rs = this.data.items, v = 0;
11766 start = start || 0;
11767 end = (end || end === 0) ? end : rs.length-1;
11769 for(var i = start; i <= end; i++){
11770 v += (rs[i].data[property] || 0);
11776 * Filter the records by a specified property.
11777 * @param {String} field A field on your records
11778 * @param {String/RegExp} value Either a string that the field
11779 * should start with or a RegExp to test against the field
11780 * @param {Boolean} anyMatch True to match any part not just the beginning
11782 filter : function(property, value, anyMatch){
11783 var fn = this.createFilterFn(property, value, anyMatch);
11784 return fn ? this.filterBy(fn) : this.clearFilter();
11788 * Filter by a function. The specified function will be called with each
11789 * record in this data source. If the function returns true the record is included,
11790 * otherwise it is filtered.
11791 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11792 * @param {Object} scope (optional) The scope of the function (defaults to this)
11794 filterBy : function(fn, scope){
11795 this.snapshot = this.snapshot || this.data;
11796 this.data = this.queryBy(fn, scope||this);
11797 this.fireEvent("datachanged", this);
11801 * Query the records by a specified property.
11802 * @param {String} field A field on your records
11803 * @param {String/RegExp} value Either a string that the field
11804 * should start with or a RegExp to test against the field
11805 * @param {Boolean} anyMatch True to match any part not just the beginning
11806 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11808 query : function(property, value, anyMatch){
11809 var fn = this.createFilterFn(property, value, anyMatch);
11810 return fn ? this.queryBy(fn) : this.data.clone();
11814 * Query by a function. The specified function will be called with each
11815 * record in this data source. If the function returns true the record is included
11817 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11818 * @param {Object} scope (optional) The scope of the function (defaults to this)
11819 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11821 queryBy : function(fn, scope){
11822 var data = this.snapshot || this.data;
11823 return data.filterBy(fn, scope||this);
11827 * Collects unique values for a particular dataIndex from this store.
11828 * @param {String} dataIndex The property to collect
11829 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11830 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11831 * @return {Array} An array of the unique values
11833 collect : function(dataIndex, allowNull, bypassFilter){
11834 var d = (bypassFilter === true && this.snapshot) ?
11835 this.snapshot.items : this.data.items;
11836 var v, sv, r = [], l = {};
11837 for(var i = 0, len = d.length; i < len; i++){
11838 v = d[i].data[dataIndex];
11840 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11849 * Revert to a view of the Record cache with no filtering applied.
11850 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11852 clearFilter : function(suppressEvent){
11853 if(this.snapshot && this.snapshot != this.data){
11854 this.data = this.snapshot;
11855 delete this.snapshot;
11856 if(suppressEvent !== true){
11857 this.fireEvent("datachanged", this);
11863 afterEdit : function(record){
11864 if(this.modified.indexOf(record) == -1){
11865 this.modified.push(record);
11867 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11871 afterReject : function(record){
11872 this.modified.remove(record);
11873 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11877 afterCommit : function(record){
11878 this.modified.remove(record);
11879 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11883 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11884 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11886 commitChanges : function(){
11887 var m = this.modified.slice(0);
11888 this.modified = [];
11889 for(var i = 0, len = m.length; i < len; i++){
11895 * Cancel outstanding changes on all changed records.
11897 rejectChanges : function(){
11898 var m = this.modified.slice(0);
11899 this.modified = [];
11900 for(var i = 0, len = m.length; i < len; i++){
11905 onMetaChange : function(meta, rtype, o){
11906 this.recordType = rtype;
11907 this.fields = rtype.prototype.fields;
11908 delete this.snapshot;
11909 this.sortInfo = meta.sortInfo || this.sortInfo;
11910 this.modified = [];
11911 this.fireEvent('metachange', this, this.reader.meta);
11914 moveIndex : function(data, type)
11916 var index = this.indexOf(data);
11918 var newIndex = index + type;
11922 this.insert(newIndex, data);
11927 * Ext JS Library 1.1.1
11928 * Copyright(c) 2006-2007, Ext JS, LLC.
11930 * Originally Released Under LGPL - original licence link has changed is not relivant.
11933 * <script type="text/javascript">
11937 * @class Roo.data.SimpleStore
11938 * @extends Roo.data.Store
11939 * Small helper class to make creating Stores from Array data easier.
11940 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11941 * @cfg {Array} fields An array of field definition objects, or field name strings.
11942 * @cfg {Array} data The multi-dimensional array of data
11944 * @param {Object} config
11946 Roo.data.SimpleStore = function(config){
11947 Roo.data.SimpleStore.superclass.constructor.call(this, {
11949 reader: new Roo.data.ArrayReader({
11952 Roo.data.Record.create(config.fields)
11954 proxy : new Roo.data.MemoryProxy(config.data)
11958 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11960 * Ext JS Library 1.1.1
11961 * Copyright(c) 2006-2007, Ext JS, LLC.
11963 * Originally Released Under LGPL - original licence link has changed is not relivant.
11966 * <script type="text/javascript">
11971 * @extends Roo.data.Store
11972 * @class Roo.data.JsonStore
11973 * Small helper class to make creating Stores for JSON data easier. <br/>
11975 var store = new Roo.data.JsonStore({
11976 url: 'get-images.php',
11978 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11981 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11982 * JsonReader and HttpProxy (unless inline data is provided).</b>
11983 * @cfg {Array} fields An array of field definition objects, or field name strings.
11985 * @param {Object} config
11987 Roo.data.JsonStore = function(c){
11988 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11989 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11990 reader: new Roo.data.JsonReader(c, c.fields)
11993 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11995 * Ext JS Library 1.1.1
11996 * Copyright(c) 2006-2007, Ext JS, LLC.
11998 * Originally Released Under LGPL - original licence link has changed is not relivant.
12001 * <script type="text/javascript">
12005 Roo.data.Field = function(config){
12006 if(typeof config == "string"){
12007 config = {name: config};
12009 Roo.apply(this, config);
12012 this.type = "auto";
12015 var st = Roo.data.SortTypes;
12016 // named sortTypes are supported, here we look them up
12017 if(typeof this.sortType == "string"){
12018 this.sortType = st[this.sortType];
12021 // set default sortType for strings and dates
12022 if(!this.sortType){
12025 this.sortType = st.asUCString;
12028 this.sortType = st.asDate;
12031 this.sortType = st.none;
12036 var stripRe = /[\$,%]/g;
12038 // prebuilt conversion function for this field, instead of
12039 // switching every time we're reading a value
12041 var cv, dateFormat = this.dateFormat;
12046 cv = function(v){ return v; };
12049 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12053 return v !== undefined && v !== null && v !== '' ?
12054 parseInt(String(v).replace(stripRe, ""), 10) : '';
12059 return v !== undefined && v !== null && v !== '' ?
12060 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12065 cv = function(v){ return v === true || v === "true" || v == 1; };
12072 if(v instanceof Date){
12076 if(dateFormat == "timestamp"){
12077 return new Date(v*1000);
12079 return Date.parseDate(v, dateFormat);
12081 var parsed = Date.parse(v);
12082 return parsed ? new Date(parsed) : null;
12091 Roo.data.Field.prototype = {
12099 * Ext JS Library 1.1.1
12100 * Copyright(c) 2006-2007, Ext JS, LLC.
12102 * Originally Released Under LGPL - original licence link has changed is not relivant.
12105 * <script type="text/javascript">
12108 // Base class for reading structured data from a data source. This class is intended to be
12109 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12112 * @class Roo.data.DataReader
12113 * Base class for reading structured data from a data source. This class is intended to be
12114 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12117 Roo.data.DataReader = function(meta, recordType){
12121 this.recordType = recordType instanceof Array ?
12122 Roo.data.Record.create(recordType) : recordType;
12125 Roo.data.DataReader.prototype = {
12127 * Create an empty record
12128 * @param {Object} data (optional) - overlay some values
12129 * @return {Roo.data.Record} record created.
12131 newRow : function(d) {
12133 this.recordType.prototype.fields.each(function(c) {
12135 case 'int' : da[c.name] = 0; break;
12136 case 'date' : da[c.name] = new Date(); break;
12137 case 'float' : da[c.name] = 0.0; break;
12138 case 'boolean' : da[c.name] = false; break;
12139 default : da[c.name] = ""; break;
12143 return new this.recordType(Roo.apply(da, d));
12148 * Ext JS Library 1.1.1
12149 * Copyright(c) 2006-2007, Ext JS, LLC.
12151 * Originally Released Under LGPL - original licence link has changed is not relivant.
12154 * <script type="text/javascript">
12158 * @class Roo.data.DataProxy
12159 * @extends Roo.data.Observable
12160 * This class is an abstract base class for implementations which provide retrieval of
12161 * unformatted data objects.<br>
12163 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12164 * (of the appropriate type which knows how to parse the data object) to provide a block of
12165 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12167 * Custom implementations must implement the load method as described in
12168 * {@link Roo.data.HttpProxy#load}.
12170 Roo.data.DataProxy = function(){
12173 * @event beforeload
12174 * Fires before a network request is made to retrieve a data object.
12175 * @param {Object} This DataProxy object.
12176 * @param {Object} params The params parameter to the load function.
12181 * Fires before the load method's callback is called.
12182 * @param {Object} This DataProxy object.
12183 * @param {Object} o The data object.
12184 * @param {Object} arg The callback argument object passed to the load function.
12188 * @event loadexception
12189 * Fires if an Exception occurs during data retrieval.
12190 * @param {Object} This DataProxy object.
12191 * @param {Object} o The data object.
12192 * @param {Object} arg The callback argument object passed to the load function.
12193 * @param {Object} e The Exception.
12195 loadexception : true
12197 Roo.data.DataProxy.superclass.constructor.call(this);
12200 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12203 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12207 * Ext JS Library 1.1.1
12208 * Copyright(c) 2006-2007, Ext JS, LLC.
12210 * Originally Released Under LGPL - original licence link has changed is not relivant.
12213 * <script type="text/javascript">
12216 * @class Roo.data.MemoryProxy
12217 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12218 * to the Reader when its load method is called.
12220 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12222 Roo.data.MemoryProxy = function(data){
12226 Roo.data.MemoryProxy.superclass.constructor.call(this);
12230 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12233 * Load data from the requested source (in this case an in-memory
12234 * data object passed to the constructor), read the data object into
12235 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12236 * process that block using the passed callback.
12237 * @param {Object} params This parameter is not used by the MemoryProxy class.
12238 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12239 * object into a block of Roo.data.Records.
12240 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12241 * The function must be passed <ul>
12242 * <li>The Record block object</li>
12243 * <li>The "arg" argument from the load function</li>
12244 * <li>A boolean success indicator</li>
12246 * @param {Object} scope The scope in which to call the callback
12247 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12249 load : function(params, reader, callback, scope, arg){
12250 params = params || {};
12253 result = reader.readRecords(this.data);
12255 this.fireEvent("loadexception", this, arg, null, e);
12256 callback.call(scope, null, arg, false);
12259 callback.call(scope, result, arg, true);
12263 update : function(params, records){
12268 * Ext JS Library 1.1.1
12269 * Copyright(c) 2006-2007, Ext JS, LLC.
12271 * Originally Released Under LGPL - original licence link has changed is not relivant.
12274 * <script type="text/javascript">
12277 * @class Roo.data.HttpProxy
12278 * @extends Roo.data.DataProxy
12279 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12280 * configured to reference a certain URL.<br><br>
12282 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12283 * from which the running page was served.<br><br>
12285 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12287 * Be aware that to enable the browser to parse an XML document, the server must set
12288 * the Content-Type header in the HTTP response to "text/xml".
12290 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12291 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12292 * will be used to make the request.
12294 Roo.data.HttpProxy = function(conn){
12295 Roo.data.HttpProxy.superclass.constructor.call(this);
12296 // is conn a conn config or a real conn?
12298 this.useAjax = !conn || !conn.events;
12302 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12303 // thse are take from connection...
12306 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12309 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12310 * extra parameters to each request made by this object. (defaults to undefined)
12313 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12314 * to each request made by this object. (defaults to undefined)
12317 * @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)
12320 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12323 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12329 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12333 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12334 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12335 * a finer-grained basis than the DataProxy events.
12337 getConnection : function(){
12338 return this.useAjax ? Roo.Ajax : this.conn;
12342 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12343 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12344 * process that block using the passed callback.
12345 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12346 * for the request to the remote server.
12347 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12348 * object into a block of Roo.data.Records.
12349 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12350 * The function must be passed <ul>
12351 * <li>The Record block object</li>
12352 * <li>The "arg" argument from the load function</li>
12353 * <li>A boolean success indicator</li>
12355 * @param {Object} scope The scope in which to call the callback
12356 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12358 load : function(params, reader, callback, scope, arg){
12359 if(this.fireEvent("beforeload", this, params) !== false){
12361 params : params || {},
12363 callback : callback,
12368 callback : this.loadResponse,
12372 Roo.applyIf(o, this.conn);
12373 if(this.activeRequest){
12374 Roo.Ajax.abort(this.activeRequest);
12376 this.activeRequest = Roo.Ajax.request(o);
12378 this.conn.request(o);
12381 callback.call(scope||this, null, arg, false);
12386 loadResponse : function(o, success, response){
12387 delete this.activeRequest;
12389 this.fireEvent("loadexception", this, o, response);
12390 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12395 result = o.reader.read(response);
12397 this.fireEvent("loadexception", this, o, response, e);
12398 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12402 this.fireEvent("load", this, o, o.request.arg);
12403 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12407 update : function(dataSet){
12412 updateResponse : function(dataSet){
12417 * Ext JS Library 1.1.1
12418 * Copyright(c) 2006-2007, Ext JS, LLC.
12420 * Originally Released Under LGPL - original licence link has changed is not relivant.
12423 * <script type="text/javascript">
12427 * @class Roo.data.ScriptTagProxy
12428 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12429 * other than the originating domain of the running page.<br><br>
12431 * <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
12432 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12434 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12435 * source code that is used as the source inside a <script> tag.<br><br>
12437 * In order for the browser to process the returned data, the server must wrap the data object
12438 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12439 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12440 * depending on whether the callback name was passed:
12443 boolean scriptTag = false;
12444 String cb = request.getParameter("callback");
12447 response.setContentType("text/javascript");
12449 response.setContentType("application/x-json");
12451 Writer out = response.getWriter();
12453 out.write(cb + "(");
12455 out.print(dataBlock.toJsonString());
12462 * @param {Object} config A configuration object.
12464 Roo.data.ScriptTagProxy = function(config){
12465 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12466 Roo.apply(this, config);
12467 this.head = document.getElementsByTagName("head")[0];
12470 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12472 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12474 * @cfg {String} url The URL from which to request the data object.
12477 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12481 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12482 * the server the name of the callback function set up by the load call to process the returned data object.
12483 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12484 * javascript output which calls this named function passing the data object as its only parameter.
12486 callbackParam : "callback",
12488 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12489 * name to the request.
12494 * Load data from the configured URL, read the data object into
12495 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12496 * process that block using the passed callback.
12497 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12498 * for the request to the remote server.
12499 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12500 * object into a block of Roo.data.Records.
12501 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12502 * The function must be passed <ul>
12503 * <li>The Record block object</li>
12504 * <li>The "arg" argument from the load function</li>
12505 * <li>A boolean success indicator</li>
12507 * @param {Object} scope The scope in which to call the callback
12508 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12510 load : function(params, reader, callback, scope, arg){
12511 if(this.fireEvent("beforeload", this, params) !== false){
12513 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12515 var url = this.url;
12516 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12518 url += "&_dc=" + (new Date().getTime());
12520 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12523 cb : "stcCallback"+transId,
12524 scriptId : "stcScript"+transId,
12528 callback : callback,
12534 window[trans.cb] = function(o){
12535 conn.handleResponse(o, trans);
12538 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12540 if(this.autoAbort !== false){
12544 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12546 var script = document.createElement("script");
12547 script.setAttribute("src", url);
12548 script.setAttribute("type", "text/javascript");
12549 script.setAttribute("id", trans.scriptId);
12550 this.head.appendChild(script);
12552 this.trans = trans;
12554 callback.call(scope||this, null, arg, false);
12559 isLoading : function(){
12560 return this.trans ? true : false;
12564 * Abort the current server request.
12566 abort : function(){
12567 if(this.isLoading()){
12568 this.destroyTrans(this.trans);
12573 destroyTrans : function(trans, isLoaded){
12574 this.head.removeChild(document.getElementById(trans.scriptId));
12575 clearTimeout(trans.timeoutId);
12577 window[trans.cb] = undefined;
12579 delete window[trans.cb];
12582 // if hasn't been loaded, wait for load to remove it to prevent script error
12583 window[trans.cb] = function(){
12584 window[trans.cb] = undefined;
12586 delete window[trans.cb];
12593 handleResponse : function(o, trans){
12594 this.trans = false;
12595 this.destroyTrans(trans, true);
12598 result = trans.reader.readRecords(o);
12600 this.fireEvent("loadexception", this, o, trans.arg, e);
12601 trans.callback.call(trans.scope||window, null, trans.arg, false);
12604 this.fireEvent("load", this, o, trans.arg);
12605 trans.callback.call(trans.scope||window, result, trans.arg, true);
12609 handleFailure : function(trans){
12610 this.trans = false;
12611 this.destroyTrans(trans, false);
12612 this.fireEvent("loadexception", this, null, trans.arg);
12613 trans.callback.call(trans.scope||window, null, trans.arg, false);
12617 * Ext JS Library 1.1.1
12618 * Copyright(c) 2006-2007, Ext JS, LLC.
12620 * Originally Released Under LGPL - original licence link has changed is not relivant.
12623 * <script type="text/javascript">
12627 * @class Roo.data.JsonReader
12628 * @extends Roo.data.DataReader
12629 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12630 * based on mappings in a provided Roo.data.Record constructor.
12632 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12633 * in the reply previously.
12638 var RecordDef = Roo.data.Record.create([
12639 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12640 {name: 'occupation'} // This field will use "occupation" as the mapping.
12642 var myReader = new Roo.data.JsonReader({
12643 totalProperty: "results", // The property which contains the total dataset size (optional)
12644 root: "rows", // The property which contains an Array of row objects
12645 id: "id" // The property within each row object that provides an ID for the record (optional)
12649 * This would consume a JSON file like this:
12651 { 'results': 2, 'rows': [
12652 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12653 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12656 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12657 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12658 * paged from the remote server.
12659 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12660 * @cfg {String} root name of the property which contains the Array of row objects.
12661 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12662 * @cfg {Array} fields Array of field definition objects
12664 * Create a new JsonReader
12665 * @param {Object} meta Metadata configuration options
12666 * @param {Object} recordType Either an Array of field definition objects,
12667 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12669 Roo.data.JsonReader = function(meta, recordType){
12672 // set some defaults:
12673 Roo.applyIf(meta, {
12674 totalProperty: 'total',
12675 successProperty : 'success',
12680 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12682 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12685 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12686 * Used by Store query builder to append _requestMeta to params.
12689 metaFromRemote : false,
12691 * This method is only used by a DataProxy which has retrieved data from a remote server.
12692 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12693 * @return {Object} data A data block which is used by an Roo.data.Store object as
12694 * a cache of Roo.data.Records.
12696 read : function(response){
12697 var json = response.responseText;
12699 var o = /* eval:var:o */ eval("("+json+")");
12701 throw {message: "JsonReader.read: Json object not found"};
12707 this.metaFromRemote = true;
12708 this.meta = o.metaData;
12709 this.recordType = Roo.data.Record.create(o.metaData.fields);
12710 this.onMetaChange(this.meta, this.recordType, o);
12712 return this.readRecords(o);
12715 // private function a store will implement
12716 onMetaChange : function(meta, recordType, o){
12723 simpleAccess: function(obj, subsc) {
12730 getJsonAccessor: function(){
12732 return function(expr) {
12734 return(re.test(expr))
12735 ? new Function("obj", "return obj." + expr)
12740 return Roo.emptyFn;
12745 * Create a data block containing Roo.data.Records from an XML document.
12746 * @param {Object} o An object which contains an Array of row objects in the property specified
12747 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12748 * which contains the total size of the dataset.
12749 * @return {Object} data A data block which is used by an Roo.data.Store object as
12750 * a cache of Roo.data.Records.
12752 readRecords : function(o){
12754 * After any data loads, the raw JSON data is available for further custom processing.
12758 var s = this.meta, Record = this.recordType,
12759 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12761 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12763 if(s.totalProperty) {
12764 this.getTotal = this.getJsonAccessor(s.totalProperty);
12766 if(s.successProperty) {
12767 this.getSuccess = this.getJsonAccessor(s.successProperty);
12769 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12771 var g = this.getJsonAccessor(s.id);
12772 this.getId = function(rec) {
12774 return (r === undefined || r === "") ? null : r;
12777 this.getId = function(){return null;};
12780 for(var jj = 0; jj < fl; jj++){
12782 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12783 this.ef[jj] = this.getJsonAccessor(map);
12787 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12788 if(s.totalProperty){
12789 var vt = parseInt(this.getTotal(o), 10);
12794 if(s.successProperty){
12795 var vs = this.getSuccess(o);
12796 if(vs === false || vs === 'false'){
12801 for(var i = 0; i < c; i++){
12804 var id = this.getId(n);
12805 for(var j = 0; j < fl; j++){
12807 var v = this.ef[j](n);
12809 Roo.log('missing convert for ' + f.name);
12813 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12815 var record = new Record(values, id);
12817 records[i] = record;
12823 totalRecords : totalRecords
12828 * Ext JS Library 1.1.1
12829 * Copyright(c) 2006-2007, Ext JS, LLC.
12831 * Originally Released Under LGPL - original licence link has changed is not relivant.
12834 * <script type="text/javascript">
12838 * @class Roo.data.ArrayReader
12839 * @extends Roo.data.DataReader
12840 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12841 * Each element of that Array represents a row of data fields. The
12842 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12843 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12847 var RecordDef = Roo.data.Record.create([
12848 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12849 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12851 var myReader = new Roo.data.ArrayReader({
12852 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12856 * This would consume an Array like this:
12858 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12860 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12862 * Create a new JsonReader
12863 * @param {Object} meta Metadata configuration options.
12864 * @param {Object} recordType Either an Array of field definition objects
12865 * as specified to {@link Roo.data.Record#create},
12866 * or an {@link Roo.data.Record} object
12867 * created using {@link Roo.data.Record#create}.
12869 Roo.data.ArrayReader = function(meta, recordType){
12870 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12873 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12875 * Create a data block containing Roo.data.Records from an XML document.
12876 * @param {Object} o An Array of row objects which represents the dataset.
12877 * @return {Object} data A data block which is used by an Roo.data.Store object as
12878 * a cache of Roo.data.Records.
12880 readRecords : function(o){
12881 var sid = this.meta ? this.meta.id : null;
12882 var recordType = this.recordType, fields = recordType.prototype.fields;
12885 for(var i = 0; i < root.length; i++){
12888 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12889 for(var j = 0, jlen = fields.length; j < jlen; j++){
12890 var f = fields.items[j];
12891 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12892 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12894 values[f.name] = v;
12896 var record = new recordType(values, id);
12898 records[records.length] = record;
12902 totalRecords : records.length
12911 * @class Roo.bootstrap.ComboBox
12912 * @extends Roo.bootstrap.TriggerField
12913 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12914 * @cfg {Boolean} append (true|false) default false
12915 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12916 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12917 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12918 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12919 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12920 * @cfg {Boolean} animate default true
12921 * @cfg {Boolean} emptyResultText only for touch device
12922 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12923 * @cfg {String} emptyTitle default ''
12925 * Create a new ComboBox.
12926 * @param {Object} config Configuration options
12928 Roo.bootstrap.ComboBox = function(config){
12929 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12933 * Fires when the dropdown list is expanded
12934 * @param {Roo.bootstrap.ComboBox} combo This combo box
12939 * Fires when the dropdown list is collapsed
12940 * @param {Roo.bootstrap.ComboBox} combo This combo box
12944 * @event beforeselect
12945 * Fires before a list item is selected. Return false to cancel the selection.
12946 * @param {Roo.bootstrap.ComboBox} combo This combo box
12947 * @param {Roo.data.Record} record The data record returned from the underlying store
12948 * @param {Number} index The index of the selected item in the dropdown list
12950 'beforeselect' : true,
12953 * Fires when a list item is selected
12954 * @param {Roo.bootstrap.ComboBox} combo This combo box
12955 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12956 * @param {Number} index The index of the selected item in the dropdown list
12960 * @event beforequery
12961 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12962 * The event object passed has these properties:
12963 * @param {Roo.bootstrap.ComboBox} combo This combo box
12964 * @param {String} query The query
12965 * @param {Boolean} forceAll true to force "all" query
12966 * @param {Boolean} cancel true to cancel the query
12967 * @param {Object} e The query event object
12969 'beforequery': true,
12972 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12973 * @param {Roo.bootstrap.ComboBox} combo This combo box
12978 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12979 * @param {Roo.bootstrap.ComboBox} combo This combo box
12980 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12985 * Fires when the remove value from the combobox array
12986 * @param {Roo.bootstrap.ComboBox} combo This combo box
12990 * @event afterremove
12991 * Fires when the remove value from the combobox array
12992 * @param {Roo.bootstrap.ComboBox} combo This combo box
12994 'afterremove' : true,
12996 * @event specialfilter
12997 * Fires when specialfilter
12998 * @param {Roo.bootstrap.ComboBox} combo This combo box
13000 'specialfilter' : true,
13003 * Fires when tick the element
13004 * @param {Roo.bootstrap.ComboBox} combo This combo box
13008 * @event touchviewdisplay
13009 * Fires when touch view require special display (default is using displayField)
13010 * @param {Roo.bootstrap.ComboBox} combo This combo box
13011 * @param {Object} cfg set html .
13013 'touchviewdisplay' : true
13018 this.tickItems = [];
13020 this.selectedIndex = -1;
13021 if(this.mode == 'local'){
13022 if(config.queryDelay === undefined){
13023 this.queryDelay = 10;
13025 if(config.minChars === undefined){
13031 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13034 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13035 * rendering into an Roo.Editor, defaults to false)
13038 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13039 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13042 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13045 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13046 * the dropdown list (defaults to undefined, with no header element)
13050 * @cfg {String/Roo.Template} tpl The template to use to render the output
13054 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13056 listWidth: undefined,
13058 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13059 * mode = 'remote' or 'text' if mode = 'local')
13061 displayField: undefined,
13064 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13065 * mode = 'remote' or 'value' if mode = 'local').
13066 * Note: use of a valueField requires the user make a selection
13067 * in order for a value to be mapped.
13069 valueField: undefined,
13071 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13076 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13077 * field's data value (defaults to the underlying DOM element's name)
13079 hiddenName: undefined,
13081 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13085 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13087 selectedClass: 'active',
13090 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13094 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13095 * anchor positions (defaults to 'tl-bl')
13097 listAlign: 'tl-bl?',
13099 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13103 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13104 * query specified by the allQuery config option (defaults to 'query')
13106 triggerAction: 'query',
13108 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13109 * (defaults to 4, does not apply if editable = false)
13113 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13114 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13118 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13119 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13123 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13124 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13128 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13129 * when editable = true (defaults to false)
13131 selectOnFocus:false,
13133 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13135 queryParam: 'query',
13137 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13138 * when mode = 'remote' (defaults to 'Loading...')
13140 loadingText: 'Loading...',
13142 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13146 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13150 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13151 * traditional select (defaults to true)
13155 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13159 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13163 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13164 * listWidth has a higher value)
13168 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13169 * allow the user to set arbitrary text into the field (defaults to false)
13171 forceSelection:false,
13173 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13174 * if typeAhead = true (defaults to 250)
13176 typeAheadDelay : 250,
13178 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13179 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13181 valueNotFoundText : undefined,
13183 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13185 blockFocus : false,
13188 * @cfg {Boolean} disableClear Disable showing of clear button.
13190 disableClear : false,
13192 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13194 alwaysQuery : false,
13197 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13202 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13204 invalidClass : "has-warning",
13207 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13209 validClass : "has-success",
13212 * @cfg {Boolean} specialFilter (true|false) special filter default false
13214 specialFilter : false,
13217 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13219 mobileTouchView : true,
13222 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13224 useNativeIOS : false,
13227 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13229 mobile_restrict_height : false,
13231 ios_options : false,
13243 btnPosition : 'right',
13244 triggerList : true,
13245 showToggleBtn : true,
13247 emptyResultText: 'Empty',
13248 triggerText : 'Select',
13251 // element that contains real text value.. (when hidden is used..)
13253 getAutoCreate : function()
13258 * Render classic select for iso
13261 if(Roo.isIOS && this.useNativeIOS){
13262 cfg = this.getAutoCreateNativeIOS();
13270 if(Roo.isTouch && this.mobileTouchView){
13271 cfg = this.getAutoCreateTouchView();
13278 if(!this.tickable){
13279 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13284 * ComboBox with tickable selections
13287 var align = this.labelAlign || this.parentLabelAlign();
13290 cls : 'form-group roo-combobox-tickable' //input-group
13293 var btn_text_select = '';
13294 var btn_text_done = '';
13295 var btn_text_cancel = '';
13297 if (this.btn_text_show) {
13298 btn_text_select = 'Select';
13299 btn_text_done = 'Done';
13300 btn_text_cancel = 'Cancel';
13305 cls : 'tickable-buttons',
13310 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13311 //html : this.triggerText
13312 html: btn_text_select
13318 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13320 html: btn_text_done
13326 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13328 html: btn_text_cancel
13334 buttons.cn.unshift({
13336 cls: 'roo-select2-search-field-input'
13342 Roo.each(buttons.cn, function(c){
13344 c.cls += ' btn-' + _this.size;
13347 if (_this.disabled) {
13358 cls: 'form-hidden-field'
13362 cls: 'roo-select2-choices',
13366 cls: 'roo-select2-search-field',
13377 cls: 'roo-select2-container input-group roo-select2-container-multi',
13383 // cls: 'typeahead typeahead-long dropdown-menu',
13384 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13389 if(this.hasFeedback && !this.allowBlank){
13393 cls: 'glyphicon form-control-feedback'
13396 combobox.cn.push(feedback);
13401 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13402 tooltip : 'This field is required'
13404 if (Roo.bootstrap.version == 4) {
13407 style : 'display:none'
13410 if (align ==='left' && this.fieldLabel.length) {
13412 cfg.cls += ' roo-form-group-label-left row';
13419 cls : 'control-label col-form-label',
13420 html : this.fieldLabel
13432 var labelCfg = cfg.cn[1];
13433 var contentCfg = cfg.cn[2];
13436 if(this.indicatorpos == 'right'){
13442 cls : 'control-label col-form-label',
13446 html : this.fieldLabel
13462 labelCfg = cfg.cn[0];
13463 contentCfg = cfg.cn[1];
13467 if(this.labelWidth > 12){
13468 labelCfg.style = "width: " + this.labelWidth + 'px';
13471 if(this.labelWidth < 13 && this.labelmd == 0){
13472 this.labelmd = this.labelWidth;
13475 if(this.labellg > 0){
13476 labelCfg.cls += ' col-lg-' + this.labellg;
13477 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13480 if(this.labelmd > 0){
13481 labelCfg.cls += ' col-md-' + this.labelmd;
13482 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13485 if(this.labelsm > 0){
13486 labelCfg.cls += ' col-sm-' + this.labelsm;
13487 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13490 if(this.labelxs > 0){
13491 labelCfg.cls += ' col-xs-' + this.labelxs;
13492 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13496 } else if ( this.fieldLabel.length) {
13497 // Roo.log(" label");
13502 //cls : 'input-group-addon',
13503 html : this.fieldLabel
13508 if(this.indicatorpos == 'right'){
13512 //cls : 'input-group-addon',
13513 html : this.fieldLabel
13523 // Roo.log(" no label && no align");
13530 ['xs','sm','md','lg'].map(function(size){
13531 if (settings[size]) {
13532 cfg.cls += ' col-' + size + '-' + settings[size];
13540 _initEventsCalled : false,
13543 initEvents: function()
13545 if (this._initEventsCalled) { // as we call render... prevent looping...
13548 this._initEventsCalled = true;
13551 throw "can not find store for combo";
13554 this.indicator = this.indicatorEl();
13556 this.store = Roo.factory(this.store, Roo.data);
13557 this.store.parent = this;
13559 // if we are building from html. then this element is so complex, that we can not really
13560 // use the rendered HTML.
13561 // so we have to trash and replace the previous code.
13562 if (Roo.XComponent.build_from_html) {
13563 // remove this element....
13564 var e = this.el.dom, k=0;
13565 while (e ) { e = e.previousSibling; ++k;}
13570 this.rendered = false;
13572 this.render(this.parent().getChildContainer(true), k);
13575 if(Roo.isIOS && this.useNativeIOS){
13576 this.initIOSView();
13584 if(Roo.isTouch && this.mobileTouchView){
13585 this.initTouchView();
13590 this.initTickableEvents();
13594 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13596 if(this.hiddenName){
13598 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13600 this.hiddenField.dom.value =
13601 this.hiddenValue !== undefined ? this.hiddenValue :
13602 this.value !== undefined ? this.value : '';
13604 // prevent input submission
13605 this.el.dom.removeAttribute('name');
13606 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13611 // this.el.dom.setAttribute('autocomplete', 'off');
13614 var cls = 'x-combo-list';
13616 //this.list = new Roo.Layer({
13617 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13623 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13624 _this.list.setWidth(lw);
13627 this.list.on('mouseover', this.onViewOver, this);
13628 this.list.on('mousemove', this.onViewMove, this);
13629 this.list.on('scroll', this.onViewScroll, this);
13632 this.list.swallowEvent('mousewheel');
13633 this.assetHeight = 0;
13636 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13637 this.assetHeight += this.header.getHeight();
13640 this.innerList = this.list.createChild({cls:cls+'-inner'});
13641 this.innerList.on('mouseover', this.onViewOver, this);
13642 this.innerList.on('mousemove', this.onViewMove, this);
13643 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13645 if(this.allowBlank && !this.pageSize && !this.disableClear){
13646 this.footer = this.list.createChild({cls:cls+'-ft'});
13647 this.pageTb = new Roo.Toolbar(this.footer);
13651 this.footer = this.list.createChild({cls:cls+'-ft'});
13652 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13653 {pageSize: this.pageSize});
13657 if (this.pageTb && this.allowBlank && !this.disableClear) {
13659 this.pageTb.add(new Roo.Toolbar.Fill(), {
13660 cls: 'x-btn-icon x-btn-clear',
13662 handler: function()
13665 _this.clearValue();
13666 _this.onSelect(false, -1);
13671 this.assetHeight += this.footer.getHeight();
13676 this.tpl = Roo.bootstrap.version == 4 ?
13677 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13678 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13681 this.view = new Roo.View(this.list, this.tpl, {
13682 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13684 //this.view.wrapEl.setDisplayed(false);
13685 this.view.on('click', this.onViewClick, this);
13688 this.store.on('beforeload', this.onBeforeLoad, this);
13689 this.store.on('load', this.onLoad, this);
13690 this.store.on('loadexception', this.onLoadException, this);
13692 if(this.resizable){
13693 this.resizer = new Roo.Resizable(this.list, {
13694 pinned:true, handles:'se'
13696 this.resizer.on('resize', function(r, w, h){
13697 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13698 this.listWidth = w;
13699 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13700 this.restrictHeight();
13702 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13705 if(!this.editable){
13706 this.editable = true;
13707 this.setEditable(false);
13712 if (typeof(this.events.add.listeners) != 'undefined') {
13714 this.addicon = this.wrap.createChild(
13715 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13717 this.addicon.on('click', function(e) {
13718 this.fireEvent('add', this);
13721 if (typeof(this.events.edit.listeners) != 'undefined') {
13723 this.editicon = this.wrap.createChild(
13724 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13725 if (this.addicon) {
13726 this.editicon.setStyle('margin-left', '40px');
13728 this.editicon.on('click', function(e) {
13730 // we fire even if inothing is selected..
13731 this.fireEvent('edit', this, this.lastData );
13737 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13738 "up" : function(e){
13739 this.inKeyMode = true;
13743 "down" : function(e){
13744 if(!this.isExpanded()){
13745 this.onTriggerClick();
13747 this.inKeyMode = true;
13752 "enter" : function(e){
13753 // this.onViewClick();
13757 if(this.fireEvent("specialkey", this, e)){
13758 this.onViewClick(false);
13764 "esc" : function(e){
13768 "tab" : function(e){
13771 if(this.fireEvent("specialkey", this, e)){
13772 this.onViewClick(false);
13780 doRelay : function(foo, bar, hname){
13781 if(hname == 'down' || this.scope.isExpanded()){
13782 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13791 this.queryDelay = Math.max(this.queryDelay || 10,
13792 this.mode == 'local' ? 10 : 250);
13795 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13797 if(this.typeAhead){
13798 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13800 if(this.editable !== false){
13801 this.inputEl().on("keyup", this.onKeyUp, this);
13803 if(this.forceSelection){
13804 this.inputEl().on('blur', this.doForce, this);
13808 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13809 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13813 initTickableEvents: function()
13817 if(this.hiddenName){
13819 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13821 this.hiddenField.dom.value =
13822 this.hiddenValue !== undefined ? this.hiddenValue :
13823 this.value !== undefined ? this.value : '';
13825 // prevent input submission
13826 this.el.dom.removeAttribute('name');
13827 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13832 // this.list = this.el.select('ul.dropdown-menu',true).first();
13834 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13835 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13836 if(this.triggerList){
13837 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13840 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13841 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13843 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13844 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13846 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13847 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13849 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13850 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13851 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13854 this.cancelBtn.hide();
13859 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13860 _this.list.setWidth(lw);
13863 this.list.on('mouseover', this.onViewOver, this);
13864 this.list.on('mousemove', this.onViewMove, this);
13866 this.list.on('scroll', this.onViewScroll, this);
13869 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13870 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13873 this.view = new Roo.View(this.list, this.tpl, {
13878 selectedClass: this.selectedClass
13881 //this.view.wrapEl.setDisplayed(false);
13882 this.view.on('click', this.onViewClick, this);
13886 this.store.on('beforeload', this.onBeforeLoad, this);
13887 this.store.on('load', this.onLoad, this);
13888 this.store.on('loadexception', this.onLoadException, this);
13891 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13892 "up" : function(e){
13893 this.inKeyMode = true;
13897 "down" : function(e){
13898 this.inKeyMode = true;
13902 "enter" : function(e){
13903 if(this.fireEvent("specialkey", this, e)){
13904 this.onViewClick(false);
13910 "esc" : function(e){
13911 this.onTickableFooterButtonClick(e, false, false);
13914 "tab" : function(e){
13915 this.fireEvent("specialkey", this, e);
13917 this.onTickableFooterButtonClick(e, false, false);
13924 doRelay : function(e, fn, key){
13925 if(this.scope.isExpanded()){
13926 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13935 this.queryDelay = Math.max(this.queryDelay || 10,
13936 this.mode == 'local' ? 10 : 250);
13939 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13941 if(this.typeAhead){
13942 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13945 if(this.editable !== false){
13946 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13949 this.indicator = this.indicatorEl();
13951 if(this.indicator){
13952 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13953 this.indicator.hide();
13958 onDestroy : function(){
13960 this.view.setStore(null);
13961 this.view.el.removeAllListeners();
13962 this.view.el.remove();
13963 this.view.purgeListeners();
13966 this.list.dom.innerHTML = '';
13970 this.store.un('beforeload', this.onBeforeLoad, this);
13971 this.store.un('load', this.onLoad, this);
13972 this.store.un('loadexception', this.onLoadException, this);
13974 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13978 fireKey : function(e){
13979 if(e.isNavKeyPress() && !this.list.isVisible()){
13980 this.fireEvent("specialkey", this, e);
13985 onResize: function(w, h){
13986 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13988 // if(typeof w != 'number'){
13989 // // we do not handle it!?!?
13992 // var tw = this.trigger.getWidth();
13993 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13994 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13996 // this.inputEl().setWidth( this.adjustWidth('input', x));
13998 // //this.trigger.setStyle('left', x+'px');
14000 // if(this.list && this.listWidth === undefined){
14001 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14002 // this.list.setWidth(lw);
14003 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14011 * Allow or prevent the user from directly editing the field text. If false is passed,
14012 * the user will only be able to select from the items defined in the dropdown list. This method
14013 * is the runtime equivalent of setting the 'editable' config option at config time.
14014 * @param {Boolean} value True to allow the user to directly edit the field text
14016 setEditable : function(value){
14017 if(value == this.editable){
14020 this.editable = value;
14022 this.inputEl().dom.setAttribute('readOnly', true);
14023 this.inputEl().on('mousedown', this.onTriggerClick, this);
14024 this.inputEl().addClass('x-combo-noedit');
14026 this.inputEl().dom.setAttribute('readOnly', false);
14027 this.inputEl().un('mousedown', this.onTriggerClick, this);
14028 this.inputEl().removeClass('x-combo-noedit');
14034 onBeforeLoad : function(combo,opts){
14035 if(!this.hasFocus){
14039 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14041 this.restrictHeight();
14042 this.selectedIndex = -1;
14046 onLoad : function(){
14048 this.hasQuery = false;
14050 if(!this.hasFocus){
14054 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14055 this.loading.hide();
14058 if(this.store.getCount() > 0){
14061 this.restrictHeight();
14062 if(this.lastQuery == this.allQuery){
14063 if(this.editable && !this.tickable){
14064 this.inputEl().dom.select();
14068 !this.selectByValue(this.value, true) &&
14071 !this.store.lastOptions ||
14072 typeof(this.store.lastOptions.add) == 'undefined' ||
14073 this.store.lastOptions.add != true
14076 this.select(0, true);
14079 if(this.autoFocus){
14082 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14083 this.taTask.delay(this.typeAheadDelay);
14087 this.onEmptyResults();
14093 onLoadException : function()
14095 this.hasQuery = false;
14097 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14098 this.loading.hide();
14101 if(this.tickable && this.editable){
14106 // only causes errors at present
14107 //Roo.log(this.store.reader.jsonData);
14108 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14110 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14116 onTypeAhead : function(){
14117 if(this.store.getCount() > 0){
14118 var r = this.store.getAt(0);
14119 var newValue = r.data[this.displayField];
14120 var len = newValue.length;
14121 var selStart = this.getRawValue().length;
14123 if(selStart != len){
14124 this.setRawValue(newValue);
14125 this.selectText(selStart, newValue.length);
14131 onSelect : function(record, index){
14133 if(this.fireEvent('beforeselect', this, record, index) !== false){
14135 this.setFromData(index > -1 ? record.data : false);
14138 this.fireEvent('select', this, record, index);
14143 * Returns the currently selected field value or empty string if no value is set.
14144 * @return {String} value The selected value
14146 getValue : function()
14148 if(Roo.isIOS && this.useNativeIOS){
14149 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14153 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14156 if(this.valueField){
14157 return typeof this.value != 'undefined' ? this.value : '';
14159 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14163 getRawValue : function()
14165 if(Roo.isIOS && this.useNativeIOS){
14166 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14169 var v = this.inputEl().getValue();
14175 * Clears any text/value currently set in the field
14177 clearValue : function(){
14179 if(this.hiddenField){
14180 this.hiddenField.dom.value = '';
14183 this.setRawValue('');
14184 this.lastSelectionText = '';
14185 this.lastData = false;
14187 var close = this.closeTriggerEl();
14198 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14199 * will be displayed in the field. If the value does not match the data value of an existing item,
14200 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14201 * Otherwise the field will be blank (although the value will still be set).
14202 * @param {String} value The value to match
14204 setValue : function(v)
14206 if(Roo.isIOS && this.useNativeIOS){
14207 this.setIOSValue(v);
14217 if(this.valueField){
14218 var r = this.findRecord(this.valueField, v);
14220 text = r.data[this.displayField];
14221 }else if(this.valueNotFoundText !== undefined){
14222 text = this.valueNotFoundText;
14225 this.lastSelectionText = text;
14226 if(this.hiddenField){
14227 this.hiddenField.dom.value = v;
14229 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14232 var close = this.closeTriggerEl();
14235 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14241 * @property {Object} the last set data for the element
14246 * Sets the value of the field based on a object which is related to the record format for the store.
14247 * @param {Object} value the value to set as. or false on reset?
14249 setFromData : function(o){
14256 var dv = ''; // display value
14257 var vv = ''; // value value..
14259 if (this.displayField) {
14260 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14262 // this is an error condition!!!
14263 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14266 if(this.valueField){
14267 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14270 var close = this.closeTriggerEl();
14273 if(dv.length || vv * 1 > 0){
14275 this.blockFocus=true;
14281 if(this.hiddenField){
14282 this.hiddenField.dom.value = vv;
14284 this.lastSelectionText = dv;
14285 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14289 // no hidden field.. - we store the value in 'value', but still display
14290 // display field!!!!
14291 this.lastSelectionText = dv;
14292 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14299 reset : function(){
14300 // overridden so that last data is reset..
14307 this.setValue(this.originalValue);
14308 //this.clearInvalid();
14309 this.lastData = false;
14311 this.view.clearSelections();
14317 findRecord : function(prop, value){
14319 if(this.store.getCount() > 0){
14320 this.store.each(function(r){
14321 if(r.data[prop] == value){
14331 getName: function()
14333 // returns hidden if it's set..
14334 if (!this.rendered) {return ''};
14335 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14339 onViewMove : function(e, t){
14340 this.inKeyMode = false;
14344 onViewOver : function(e, t){
14345 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14348 var item = this.view.findItemFromChild(t);
14351 var index = this.view.indexOf(item);
14352 this.select(index, false);
14357 onViewClick : function(view, doFocus, el, e)
14359 var index = this.view.getSelectedIndexes()[0];
14361 var r = this.store.getAt(index);
14365 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14372 Roo.each(this.tickItems, function(v,k){
14374 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14376 _this.tickItems.splice(k, 1);
14378 if(typeof(e) == 'undefined' && view == false){
14379 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14391 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14392 this.tickItems.push(r.data);
14395 if(typeof(e) == 'undefined' && view == false){
14396 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14403 this.onSelect(r, index);
14405 if(doFocus !== false && !this.blockFocus){
14406 this.inputEl().focus();
14411 restrictHeight : function(){
14412 //this.innerList.dom.style.height = '';
14413 //var inner = this.innerList.dom;
14414 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14415 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14416 //this.list.beginUpdate();
14417 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14418 this.list.alignTo(this.inputEl(), this.listAlign);
14419 this.list.alignTo(this.inputEl(), this.listAlign);
14420 //this.list.endUpdate();
14424 onEmptyResults : function(){
14426 if(this.tickable && this.editable){
14427 this.hasFocus = false;
14428 this.restrictHeight();
14436 * Returns true if the dropdown list is expanded, else false.
14438 isExpanded : function(){
14439 return this.list.isVisible();
14443 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14444 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14445 * @param {String} value The data value of the item to select
14446 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14447 * selected item if it is not currently in view (defaults to true)
14448 * @return {Boolean} True if the value matched an item in the list, else false
14450 selectByValue : function(v, scrollIntoView){
14451 if(v !== undefined && v !== null){
14452 var r = this.findRecord(this.valueField || this.displayField, v);
14454 this.select(this.store.indexOf(r), scrollIntoView);
14462 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14463 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14464 * @param {Number} index The zero-based index of the list item to select
14465 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14466 * selected item if it is not currently in view (defaults to true)
14468 select : function(index, scrollIntoView){
14469 this.selectedIndex = index;
14470 this.view.select(index);
14471 if(scrollIntoView !== false){
14472 var el = this.view.getNode(index);
14474 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14477 this.list.scrollChildIntoView(el, false);
14483 selectNext : function(){
14484 var ct = this.store.getCount();
14486 if(this.selectedIndex == -1){
14488 }else if(this.selectedIndex < ct-1){
14489 this.select(this.selectedIndex+1);
14495 selectPrev : function(){
14496 var ct = this.store.getCount();
14498 if(this.selectedIndex == -1){
14500 }else if(this.selectedIndex != 0){
14501 this.select(this.selectedIndex-1);
14507 onKeyUp : function(e){
14508 if(this.editable !== false && !e.isSpecialKey()){
14509 this.lastKey = e.getKey();
14510 this.dqTask.delay(this.queryDelay);
14515 validateBlur : function(){
14516 return !this.list || !this.list.isVisible();
14520 initQuery : function(){
14522 var v = this.getRawValue();
14524 if(this.tickable && this.editable){
14525 v = this.tickableInputEl().getValue();
14532 doForce : function(){
14533 if(this.inputEl().dom.value.length > 0){
14534 this.inputEl().dom.value =
14535 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14541 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14542 * query allowing the query action to be canceled if needed.
14543 * @param {String} query The SQL query to execute
14544 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14545 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14546 * saved in the current store (defaults to false)
14548 doQuery : function(q, forceAll){
14550 if(q === undefined || q === null){
14555 forceAll: forceAll,
14559 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14564 forceAll = qe.forceAll;
14565 if(forceAll === true || (q.length >= this.minChars)){
14567 this.hasQuery = true;
14569 if(this.lastQuery != q || this.alwaysQuery){
14570 this.lastQuery = q;
14571 if(this.mode == 'local'){
14572 this.selectedIndex = -1;
14574 this.store.clearFilter();
14577 if(this.specialFilter){
14578 this.fireEvent('specialfilter', this);
14583 this.store.filter(this.displayField, q);
14586 this.store.fireEvent("datachanged", this.store);
14593 this.store.baseParams[this.queryParam] = q;
14595 var options = {params : this.getParams(q)};
14598 options.add = true;
14599 options.params.start = this.page * this.pageSize;
14602 this.store.load(options);
14605 * this code will make the page width larger, at the beginning, the list not align correctly,
14606 * we should expand the list on onLoad
14607 * so command out it
14612 this.selectedIndex = -1;
14617 this.loadNext = false;
14621 getParams : function(q){
14623 //p[this.queryParam] = q;
14627 p.limit = this.pageSize;
14633 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14635 collapse : function(){
14636 if(!this.isExpanded()){
14642 this.hasFocus = false;
14646 this.cancelBtn.hide();
14647 this.trigger.show();
14650 this.tickableInputEl().dom.value = '';
14651 this.tickableInputEl().blur();
14656 Roo.get(document).un('mousedown', this.collapseIf, this);
14657 Roo.get(document).un('mousewheel', this.collapseIf, this);
14658 if (!this.editable) {
14659 Roo.get(document).un('keydown', this.listKeyPress, this);
14661 this.fireEvent('collapse', this);
14667 collapseIf : function(e){
14668 var in_combo = e.within(this.el);
14669 var in_list = e.within(this.list);
14670 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14672 if (in_combo || in_list || is_list) {
14673 //e.stopPropagation();
14678 this.onTickableFooterButtonClick(e, false, false);
14686 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14688 expand : function(){
14690 if(this.isExpanded() || !this.hasFocus){
14694 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14695 this.list.setWidth(lw);
14701 this.restrictHeight();
14705 this.tickItems = Roo.apply([], this.item);
14708 this.cancelBtn.show();
14709 this.trigger.hide();
14712 this.tickableInputEl().focus();
14717 Roo.get(document).on('mousedown', this.collapseIf, this);
14718 Roo.get(document).on('mousewheel', this.collapseIf, this);
14719 if (!this.editable) {
14720 Roo.get(document).on('keydown', this.listKeyPress, this);
14723 this.fireEvent('expand', this);
14727 // Implements the default empty TriggerField.onTriggerClick function
14728 onTriggerClick : function(e)
14730 Roo.log('trigger click');
14732 if(this.disabled || !this.triggerList){
14737 this.loadNext = false;
14739 if(this.isExpanded()){
14741 if (!this.blockFocus) {
14742 this.inputEl().focus();
14746 this.hasFocus = true;
14747 if(this.triggerAction == 'all') {
14748 this.doQuery(this.allQuery, true);
14750 this.doQuery(this.getRawValue());
14752 if (!this.blockFocus) {
14753 this.inputEl().focus();
14758 onTickableTriggerClick : function(e)
14765 this.loadNext = false;
14766 this.hasFocus = true;
14768 if(this.triggerAction == 'all') {
14769 this.doQuery(this.allQuery, true);
14771 this.doQuery(this.getRawValue());
14775 onSearchFieldClick : function(e)
14777 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14778 this.onTickableFooterButtonClick(e, false, false);
14782 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14787 this.loadNext = false;
14788 this.hasFocus = true;
14790 if(this.triggerAction == 'all') {
14791 this.doQuery(this.allQuery, true);
14793 this.doQuery(this.getRawValue());
14797 listKeyPress : function(e)
14799 //Roo.log('listkeypress');
14800 // scroll to first matching element based on key pres..
14801 if (e.isSpecialKey()) {
14804 var k = String.fromCharCode(e.getKey()).toUpperCase();
14807 var csel = this.view.getSelectedNodes();
14808 var cselitem = false;
14810 var ix = this.view.indexOf(csel[0]);
14811 cselitem = this.store.getAt(ix);
14812 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14818 this.store.each(function(v) {
14820 // start at existing selection.
14821 if (cselitem.id == v.id) {
14827 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14828 match = this.store.indexOf(v);
14834 if (match === false) {
14835 return true; // no more action?
14838 this.view.select(match);
14839 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14840 sn.scrollIntoView(sn.dom.parentNode, false);
14843 onViewScroll : function(e, t){
14845 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){
14849 this.hasQuery = true;
14851 this.loading = this.list.select('.loading', true).first();
14853 if(this.loading === null){
14854 this.list.createChild({
14856 cls: 'loading roo-select2-more-results roo-select2-active',
14857 html: 'Loading more results...'
14860 this.loading = this.list.select('.loading', true).first();
14862 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14864 this.loading.hide();
14867 this.loading.show();
14872 this.loadNext = true;
14874 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14879 addItem : function(o)
14881 var dv = ''; // display value
14883 if (this.displayField) {
14884 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14886 // this is an error condition!!!
14887 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14894 var choice = this.choices.createChild({
14896 cls: 'roo-select2-search-choice',
14905 cls: 'roo-select2-search-choice-close fa fa-times',
14910 }, this.searchField);
14912 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14914 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14922 this.inputEl().dom.value = '';
14927 onRemoveItem : function(e, _self, o)
14929 e.preventDefault();
14931 this.lastItem = Roo.apply([], this.item);
14933 var index = this.item.indexOf(o.data) * 1;
14936 Roo.log('not this item?!');
14940 this.item.splice(index, 1);
14945 this.fireEvent('remove', this, e);
14951 syncValue : function()
14953 if(!this.item.length){
14960 Roo.each(this.item, function(i){
14961 if(_this.valueField){
14962 value.push(i[_this.valueField]);
14969 this.value = value.join(',');
14971 if(this.hiddenField){
14972 this.hiddenField.dom.value = this.value;
14975 this.store.fireEvent("datachanged", this.store);
14980 clearItem : function()
14982 if(!this.multiple){
14988 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14996 if(this.tickable && !Roo.isTouch){
14997 this.view.refresh();
15001 inputEl: function ()
15003 if(Roo.isIOS && this.useNativeIOS){
15004 return this.el.select('select.roo-ios-select', true).first();
15007 if(Roo.isTouch && this.mobileTouchView){
15008 return this.el.select('input.form-control',true).first();
15012 return this.searchField;
15015 return this.el.select('input.form-control',true).first();
15018 onTickableFooterButtonClick : function(e, btn, el)
15020 e.preventDefault();
15022 this.lastItem = Roo.apply([], this.item);
15024 if(btn && btn.name == 'cancel'){
15025 this.tickItems = Roo.apply([], this.item);
15034 Roo.each(this.tickItems, function(o){
15042 validate : function()
15044 if(this.getVisibilityEl().hasClass('hidden')){
15048 var v = this.getRawValue();
15051 v = this.getValue();
15054 if(this.disabled || this.allowBlank || v.length){
15059 this.markInvalid();
15063 tickableInputEl : function()
15065 if(!this.tickable || !this.editable){
15066 return this.inputEl();
15069 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15073 getAutoCreateTouchView : function()
15078 cls: 'form-group' //input-group
15084 type : this.inputType,
15085 cls : 'form-control x-combo-noedit',
15086 autocomplete: 'new-password',
15087 placeholder : this.placeholder || '',
15092 input.name = this.name;
15096 input.cls += ' input-' + this.size;
15099 if (this.disabled) {
15100 input.disabled = true;
15111 inputblock.cls += ' input-group';
15113 inputblock.cn.unshift({
15115 cls : 'input-group-addon input-group-prepend input-group-text',
15120 if(this.removable && !this.multiple){
15121 inputblock.cls += ' roo-removable';
15123 inputblock.cn.push({
15126 cls : 'roo-combo-removable-btn close'
15130 if(this.hasFeedback && !this.allowBlank){
15132 inputblock.cls += ' has-feedback';
15134 inputblock.cn.push({
15136 cls: 'glyphicon form-control-feedback'
15143 inputblock.cls += (this.before) ? '' : ' input-group';
15145 inputblock.cn.push({
15147 cls : 'input-group-addon input-group-append input-group-text',
15153 var ibwrap = inputblock;
15158 cls: 'roo-select2-choices',
15162 cls: 'roo-select2-search-field',
15175 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15180 cls: 'form-hidden-field'
15186 if(!this.multiple && this.showToggleBtn){
15193 if (this.caret != false) {
15196 cls: 'fa fa-' + this.caret
15203 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15208 cls: 'combobox-clear',
15222 combobox.cls += ' roo-select2-container-multi';
15225 var align = this.labelAlign || this.parentLabelAlign();
15227 if (align ==='left' && this.fieldLabel.length) {
15232 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15233 tooltip : 'This field is required'
15237 cls : 'control-label col-form-label',
15238 html : this.fieldLabel
15249 var labelCfg = cfg.cn[1];
15250 var contentCfg = cfg.cn[2];
15253 if(this.indicatorpos == 'right'){
15258 cls : 'control-label col-form-label',
15262 html : this.fieldLabel
15266 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15267 tooltip : 'This field is required'
15280 labelCfg = cfg.cn[0];
15281 contentCfg = cfg.cn[1];
15286 if(this.labelWidth > 12){
15287 labelCfg.style = "width: " + this.labelWidth + 'px';
15290 if(this.labelWidth < 13 && this.labelmd == 0){
15291 this.labelmd = this.labelWidth;
15294 if(this.labellg > 0){
15295 labelCfg.cls += ' col-lg-' + this.labellg;
15296 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15299 if(this.labelmd > 0){
15300 labelCfg.cls += ' col-md-' + this.labelmd;
15301 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15304 if(this.labelsm > 0){
15305 labelCfg.cls += ' col-sm-' + this.labelsm;
15306 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15309 if(this.labelxs > 0){
15310 labelCfg.cls += ' col-xs-' + this.labelxs;
15311 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15315 } else if ( this.fieldLabel.length) {
15319 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15320 tooltip : 'This field is required'
15324 cls : 'control-label',
15325 html : this.fieldLabel
15336 if(this.indicatorpos == 'right'){
15340 cls : 'control-label',
15341 html : this.fieldLabel,
15345 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15346 tooltip : 'This field is required'
15363 var settings = this;
15365 ['xs','sm','md','lg'].map(function(size){
15366 if (settings[size]) {
15367 cfg.cls += ' col-' + size + '-' + settings[size];
15374 initTouchView : function()
15376 this.renderTouchView();
15378 this.touchViewEl.on('scroll', function(){
15379 this.el.dom.scrollTop = 0;
15382 this.originalValue = this.getValue();
15384 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15386 this.inputEl().on("click", this.showTouchView, this);
15387 if (this.triggerEl) {
15388 this.triggerEl.on("click", this.showTouchView, this);
15392 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15393 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15395 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15397 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15398 this.store.on('load', this.onTouchViewLoad, this);
15399 this.store.on('loadexception', this.onTouchViewLoadException, this);
15401 if(this.hiddenName){
15403 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15405 this.hiddenField.dom.value =
15406 this.hiddenValue !== undefined ? this.hiddenValue :
15407 this.value !== undefined ? this.value : '';
15409 this.el.dom.removeAttribute('name');
15410 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15414 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15415 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15418 if(this.removable && !this.multiple){
15419 var close = this.closeTriggerEl();
15421 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15422 close.on('click', this.removeBtnClick, this, close);
15426 * fix the bug in Safari iOS8
15428 this.inputEl().on("focus", function(e){
15429 document.activeElement.blur();
15432 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15439 renderTouchView : function()
15441 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15442 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15444 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15445 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15447 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15448 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15449 this.touchViewBodyEl.setStyle('overflow', 'auto');
15451 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15452 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15454 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15455 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15459 showTouchView : function()
15465 this.touchViewHeaderEl.hide();
15467 if(this.modalTitle.length){
15468 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15469 this.touchViewHeaderEl.show();
15472 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15473 this.touchViewEl.show();
15475 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15477 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15478 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15480 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15482 if(this.modalTitle.length){
15483 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15486 this.touchViewBodyEl.setHeight(bodyHeight);
15490 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15492 this.touchViewEl.addClass('in');
15495 if(this._touchViewMask){
15496 Roo.get(document.body).addClass("x-body-masked");
15497 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15498 this._touchViewMask.setStyle('z-index', 10000);
15499 this._touchViewMask.addClass('show');
15502 this.doTouchViewQuery();
15506 hideTouchView : function()
15508 this.touchViewEl.removeClass('in');
15512 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15514 this.touchViewEl.setStyle('display', 'none');
15517 if(this._touchViewMask){
15518 this._touchViewMask.removeClass('show');
15519 Roo.get(document.body).removeClass("x-body-masked");
15523 setTouchViewValue : function()
15530 Roo.each(this.tickItems, function(o){
15535 this.hideTouchView();
15538 doTouchViewQuery : function()
15547 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15551 if(!this.alwaysQuery || this.mode == 'local'){
15552 this.onTouchViewLoad();
15559 onTouchViewBeforeLoad : function(combo,opts)
15565 onTouchViewLoad : function()
15567 if(this.store.getCount() < 1){
15568 this.onTouchViewEmptyResults();
15572 this.clearTouchView();
15574 var rawValue = this.getRawValue();
15576 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15578 this.tickItems = [];
15580 this.store.data.each(function(d, rowIndex){
15581 var row = this.touchViewListGroup.createChild(template);
15583 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15584 row.addClass(d.data.cls);
15587 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15590 html : d.data[this.displayField]
15593 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15594 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15597 row.removeClass('selected');
15598 if(!this.multiple && this.valueField &&
15599 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15602 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15603 row.addClass('selected');
15606 if(this.multiple && this.valueField &&
15607 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15611 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15612 this.tickItems.push(d.data);
15615 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15619 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15621 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15623 if(this.modalTitle.length){
15624 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15627 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15629 if(this.mobile_restrict_height && listHeight < bodyHeight){
15630 this.touchViewBodyEl.setHeight(listHeight);
15635 if(firstChecked && listHeight > bodyHeight){
15636 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15641 onTouchViewLoadException : function()
15643 this.hideTouchView();
15646 onTouchViewEmptyResults : function()
15648 this.clearTouchView();
15650 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15652 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15656 clearTouchView : function()
15658 this.touchViewListGroup.dom.innerHTML = '';
15661 onTouchViewClick : function(e, el, o)
15663 e.preventDefault();
15666 var rowIndex = o.rowIndex;
15668 var r = this.store.getAt(rowIndex);
15670 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15672 if(!this.multiple){
15673 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15674 c.dom.removeAttribute('checked');
15677 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15679 this.setFromData(r.data);
15681 var close = this.closeTriggerEl();
15687 this.hideTouchView();
15689 this.fireEvent('select', this, r, rowIndex);
15694 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15695 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15696 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15700 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15701 this.addItem(r.data);
15702 this.tickItems.push(r.data);
15706 getAutoCreateNativeIOS : function()
15709 cls: 'form-group' //input-group,
15714 cls : 'roo-ios-select'
15718 combobox.name = this.name;
15721 if (this.disabled) {
15722 combobox.disabled = true;
15725 var settings = this;
15727 ['xs','sm','md','lg'].map(function(size){
15728 if (settings[size]) {
15729 cfg.cls += ' col-' + size + '-' + settings[size];
15739 initIOSView : function()
15741 this.store.on('load', this.onIOSViewLoad, this);
15746 onIOSViewLoad : function()
15748 if(this.store.getCount() < 1){
15752 this.clearIOSView();
15754 if(this.allowBlank) {
15756 var default_text = '-- SELECT --';
15758 if(this.placeholder.length){
15759 default_text = this.placeholder;
15762 if(this.emptyTitle.length){
15763 default_text += ' - ' + this.emptyTitle + ' -';
15766 var opt = this.inputEl().createChild({
15769 html : default_text
15773 o[this.valueField] = 0;
15774 o[this.displayField] = default_text;
15776 this.ios_options.push({
15783 this.store.data.each(function(d, rowIndex){
15787 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15788 html = d.data[this.displayField];
15793 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15794 value = d.data[this.valueField];
15803 if(this.value == d.data[this.valueField]){
15804 option['selected'] = true;
15807 var opt = this.inputEl().createChild(option);
15809 this.ios_options.push({
15816 this.inputEl().on('change', function(){
15817 this.fireEvent('select', this);
15822 clearIOSView: function()
15824 this.inputEl().dom.innerHTML = '';
15826 this.ios_options = [];
15829 setIOSValue: function(v)
15833 if(!this.ios_options){
15837 Roo.each(this.ios_options, function(opts){
15839 opts.el.dom.removeAttribute('selected');
15841 if(opts.data[this.valueField] != v){
15845 opts.el.dom.setAttribute('selected', true);
15851 * @cfg {Boolean} grow
15855 * @cfg {Number} growMin
15859 * @cfg {Number} growMax
15868 Roo.apply(Roo.bootstrap.ComboBox, {
15872 cls: 'modal-header',
15894 cls: 'list-group-item',
15898 cls: 'roo-combobox-list-group-item-value'
15902 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15916 listItemCheckbox : {
15918 cls: 'list-group-item',
15922 cls: 'roo-combobox-list-group-item-value'
15926 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15942 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15947 cls: 'modal-footer',
15955 cls: 'col-xs-6 text-left',
15958 cls: 'btn btn-danger roo-touch-view-cancel',
15964 cls: 'col-xs-6 text-right',
15967 cls: 'btn btn-success roo-touch-view-ok',
15978 Roo.apply(Roo.bootstrap.ComboBox, {
15980 touchViewTemplate : {
15982 cls: 'modal fade roo-combobox-touch-view',
15986 cls: 'modal-dialog',
15987 style : 'position:fixed', // we have to fix position....
15991 cls: 'modal-content',
15993 Roo.bootstrap.ComboBox.header,
15994 Roo.bootstrap.ComboBox.body,
15995 Roo.bootstrap.ComboBox.footer
16004 * Ext JS Library 1.1.1
16005 * Copyright(c) 2006-2007, Ext JS, LLC.
16007 * Originally Released Under LGPL - original licence link has changed is not relivant.
16010 * <script type="text/javascript">
16015 * @extends Roo.util.Observable
16016 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16017 * This class also supports single and multi selection modes. <br>
16018 * Create a data model bound view:
16020 var store = new Roo.data.Store(...);
16022 var view = new Roo.View({
16024 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16026 singleSelect: true,
16027 selectedClass: "ydataview-selected",
16031 // listen for node click?
16032 view.on("click", function(vw, index, node, e){
16033 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16037 dataModel.load("foobar.xml");
16039 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16041 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16042 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16044 * Note: old style constructor is still suported (container, template, config)
16047 * Create a new View
16048 * @param {Object} config The config object
16051 Roo.View = function(config, depreciated_tpl, depreciated_config){
16053 this.parent = false;
16055 if (typeof(depreciated_tpl) == 'undefined') {
16056 // new way.. - universal constructor.
16057 Roo.apply(this, config);
16058 this.el = Roo.get(this.el);
16061 this.el = Roo.get(config);
16062 this.tpl = depreciated_tpl;
16063 Roo.apply(this, depreciated_config);
16065 this.wrapEl = this.el.wrap().wrap();
16066 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16069 if(typeof(this.tpl) == "string"){
16070 this.tpl = new Roo.Template(this.tpl);
16072 // support xtype ctors..
16073 this.tpl = new Roo.factory(this.tpl, Roo);
16077 this.tpl.compile();
16082 * @event beforeclick
16083 * Fires before a click is processed. Returns false to cancel the default action.
16084 * @param {Roo.View} this
16085 * @param {Number} index The index of the target node
16086 * @param {HTMLElement} node The target node
16087 * @param {Roo.EventObject} e The raw event object
16089 "beforeclick" : true,
16092 * Fires when a template node is clicked.
16093 * @param {Roo.View} this
16094 * @param {Number} index The index of the target node
16095 * @param {HTMLElement} node The target node
16096 * @param {Roo.EventObject} e The raw event object
16101 * Fires when a template node is double clicked.
16102 * @param {Roo.View} this
16103 * @param {Number} index The index of the target node
16104 * @param {HTMLElement} node The target node
16105 * @param {Roo.EventObject} e The raw event object
16109 * @event contextmenu
16110 * Fires when a template node is right clicked.
16111 * @param {Roo.View} this
16112 * @param {Number} index The index of the target node
16113 * @param {HTMLElement} node The target node
16114 * @param {Roo.EventObject} e The raw event object
16116 "contextmenu" : true,
16118 * @event selectionchange
16119 * Fires when the selected nodes change.
16120 * @param {Roo.View} this
16121 * @param {Array} selections Array of the selected nodes
16123 "selectionchange" : true,
16126 * @event beforeselect
16127 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16128 * @param {Roo.View} this
16129 * @param {HTMLElement} node The node to be selected
16130 * @param {Array} selections Array of currently selected nodes
16132 "beforeselect" : true,
16134 * @event preparedata
16135 * Fires on every row to render, to allow you to change the data.
16136 * @param {Roo.View} this
16137 * @param {Object} data to be rendered (change this)
16139 "preparedata" : true
16147 "click": this.onClick,
16148 "dblclick": this.onDblClick,
16149 "contextmenu": this.onContextMenu,
16153 this.selections = [];
16155 this.cmp = new Roo.CompositeElementLite([]);
16157 this.store = Roo.factory(this.store, Roo.data);
16158 this.setStore(this.store, true);
16161 if ( this.footer && this.footer.xtype) {
16163 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16165 this.footer.dataSource = this.store;
16166 this.footer.container = fctr;
16167 this.footer = Roo.factory(this.footer, Roo);
16168 fctr.insertFirst(this.el);
16170 // this is a bit insane - as the paging toolbar seems to detach the el..
16171 // dom.parentNode.parentNode.parentNode
16172 // they get detached?
16176 Roo.View.superclass.constructor.call(this);
16181 Roo.extend(Roo.View, Roo.util.Observable, {
16184 * @cfg {Roo.data.Store} store Data store to load data from.
16189 * @cfg {String|Roo.Element} el The container element.
16194 * @cfg {String|Roo.Template} tpl The template used by this View
16198 * @cfg {String} dataName the named area of the template to use as the data area
16199 * Works with domtemplates roo-name="name"
16203 * @cfg {String} selectedClass The css class to add to selected nodes
16205 selectedClass : "x-view-selected",
16207 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16212 * @cfg {String} text to display on mask (default Loading)
16216 * @cfg {Boolean} multiSelect Allow multiple selection
16218 multiSelect : false,
16220 * @cfg {Boolean} singleSelect Allow single selection
16222 singleSelect: false,
16225 * @cfg {Boolean} toggleSelect - selecting
16227 toggleSelect : false,
16230 * @cfg {Boolean} tickable - selecting
16235 * Returns the element this view is bound to.
16236 * @return {Roo.Element}
16238 getEl : function(){
16239 return this.wrapEl;
16245 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16247 refresh : function(){
16248 //Roo.log('refresh');
16251 // if we are using something like 'domtemplate', then
16252 // the what gets used is:
16253 // t.applySubtemplate(NAME, data, wrapping data..)
16254 // the outer template then get' applied with
16255 // the store 'extra data'
16256 // and the body get's added to the
16257 // roo-name="data" node?
16258 // <span class='roo-tpl-{name}'></span> ?????
16262 this.clearSelections();
16263 this.el.update("");
16265 var records = this.store.getRange();
16266 if(records.length < 1) {
16268 // is this valid?? = should it render a template??
16270 this.el.update(this.emptyText);
16274 if (this.dataName) {
16275 this.el.update(t.apply(this.store.meta)); //????
16276 el = this.el.child('.roo-tpl-' + this.dataName);
16279 for(var i = 0, len = records.length; i < len; i++){
16280 var data = this.prepareData(records[i].data, i, records[i]);
16281 this.fireEvent("preparedata", this, data, i, records[i]);
16283 var d = Roo.apply({}, data);
16286 Roo.apply(d, {'roo-id' : Roo.id()});
16290 Roo.each(this.parent.item, function(item){
16291 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16294 Roo.apply(d, {'roo-data-checked' : 'checked'});
16298 html[html.length] = Roo.util.Format.trim(
16300 t.applySubtemplate(this.dataName, d, this.store.meta) :
16307 el.update(html.join(""));
16308 this.nodes = el.dom.childNodes;
16309 this.updateIndexes(0);
16314 * Function to override to reformat the data that is sent to
16315 * the template for each node.
16316 * DEPRICATED - use the preparedata event handler.
16317 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16318 * a JSON object for an UpdateManager bound view).
16320 prepareData : function(data, index, record)
16322 this.fireEvent("preparedata", this, data, index, record);
16326 onUpdate : function(ds, record){
16327 // Roo.log('on update');
16328 this.clearSelections();
16329 var index = this.store.indexOf(record);
16330 var n = this.nodes[index];
16331 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16332 n.parentNode.removeChild(n);
16333 this.updateIndexes(index, index);
16339 onAdd : function(ds, records, index)
16341 //Roo.log(['on Add', ds, records, index] );
16342 this.clearSelections();
16343 if(this.nodes.length == 0){
16347 var n = this.nodes[index];
16348 for(var i = 0, len = records.length; i < len; i++){
16349 var d = this.prepareData(records[i].data, i, records[i]);
16351 this.tpl.insertBefore(n, d);
16354 this.tpl.append(this.el, d);
16357 this.updateIndexes(index);
16360 onRemove : function(ds, record, index){
16361 // Roo.log('onRemove');
16362 this.clearSelections();
16363 var el = this.dataName ?
16364 this.el.child('.roo-tpl-' + this.dataName) :
16367 el.dom.removeChild(this.nodes[index]);
16368 this.updateIndexes(index);
16372 * Refresh an individual node.
16373 * @param {Number} index
16375 refreshNode : function(index){
16376 this.onUpdate(this.store, this.store.getAt(index));
16379 updateIndexes : function(startIndex, endIndex){
16380 var ns = this.nodes;
16381 startIndex = startIndex || 0;
16382 endIndex = endIndex || ns.length - 1;
16383 for(var i = startIndex; i <= endIndex; i++){
16384 ns[i].nodeIndex = i;
16389 * Changes the data store this view uses and refresh the view.
16390 * @param {Store} store
16392 setStore : function(store, initial){
16393 if(!initial && this.store){
16394 this.store.un("datachanged", this.refresh);
16395 this.store.un("add", this.onAdd);
16396 this.store.un("remove", this.onRemove);
16397 this.store.un("update", this.onUpdate);
16398 this.store.un("clear", this.refresh);
16399 this.store.un("beforeload", this.onBeforeLoad);
16400 this.store.un("load", this.onLoad);
16401 this.store.un("loadexception", this.onLoad);
16405 store.on("datachanged", this.refresh, this);
16406 store.on("add", this.onAdd, this);
16407 store.on("remove", this.onRemove, this);
16408 store.on("update", this.onUpdate, this);
16409 store.on("clear", this.refresh, this);
16410 store.on("beforeload", this.onBeforeLoad, this);
16411 store.on("load", this.onLoad, this);
16412 store.on("loadexception", this.onLoad, this);
16420 * onbeforeLoad - masks the loading area.
16423 onBeforeLoad : function(store,opts)
16425 //Roo.log('onBeforeLoad');
16427 this.el.update("");
16429 this.el.mask(this.mask ? this.mask : "Loading" );
16431 onLoad : function ()
16438 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16439 * @param {HTMLElement} node
16440 * @return {HTMLElement} The template node
16442 findItemFromChild : function(node){
16443 var el = this.dataName ?
16444 this.el.child('.roo-tpl-' + this.dataName,true) :
16447 if(!node || node.parentNode == el){
16450 var p = node.parentNode;
16451 while(p && p != el){
16452 if(p.parentNode == el){
16461 onClick : function(e){
16462 var item = this.findItemFromChild(e.getTarget());
16464 var index = this.indexOf(item);
16465 if(this.onItemClick(item, index, e) !== false){
16466 this.fireEvent("click", this, index, item, e);
16469 this.clearSelections();
16474 onContextMenu : function(e){
16475 var item = this.findItemFromChild(e.getTarget());
16477 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16482 onDblClick : function(e){
16483 var item = this.findItemFromChild(e.getTarget());
16485 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16489 onItemClick : function(item, index, e)
16491 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16494 if (this.toggleSelect) {
16495 var m = this.isSelected(item) ? 'unselect' : 'select';
16498 _t[m](item, true, false);
16501 if(this.multiSelect || this.singleSelect){
16502 if(this.multiSelect && e.shiftKey && this.lastSelection){
16503 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16505 this.select(item, this.multiSelect && e.ctrlKey);
16506 this.lastSelection = item;
16509 if(!this.tickable){
16510 e.preventDefault();
16518 * Get the number of selected nodes.
16521 getSelectionCount : function(){
16522 return this.selections.length;
16526 * Get the currently selected nodes.
16527 * @return {Array} An array of HTMLElements
16529 getSelectedNodes : function(){
16530 return this.selections;
16534 * Get the indexes of the selected nodes.
16537 getSelectedIndexes : function(){
16538 var indexes = [], s = this.selections;
16539 for(var i = 0, len = s.length; i < len; i++){
16540 indexes.push(s[i].nodeIndex);
16546 * Clear all selections
16547 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16549 clearSelections : function(suppressEvent){
16550 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16551 this.cmp.elements = this.selections;
16552 this.cmp.removeClass(this.selectedClass);
16553 this.selections = [];
16554 if(!suppressEvent){
16555 this.fireEvent("selectionchange", this, this.selections);
16561 * Returns true if the passed node is selected
16562 * @param {HTMLElement/Number} node The node or node index
16563 * @return {Boolean}
16565 isSelected : function(node){
16566 var s = this.selections;
16570 node = this.getNode(node);
16571 return s.indexOf(node) !== -1;
16576 * @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
16577 * @param {Boolean} keepExisting (optional) true to keep existing selections
16578 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16580 select : function(nodeInfo, keepExisting, suppressEvent){
16581 if(nodeInfo instanceof Array){
16583 this.clearSelections(true);
16585 for(var i = 0, len = nodeInfo.length; i < len; i++){
16586 this.select(nodeInfo[i], true, true);
16590 var node = this.getNode(nodeInfo);
16591 if(!node || this.isSelected(node)){
16592 return; // already selected.
16595 this.clearSelections(true);
16598 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16599 Roo.fly(node).addClass(this.selectedClass);
16600 this.selections.push(node);
16601 if(!suppressEvent){
16602 this.fireEvent("selectionchange", this, this.selections);
16610 * @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
16611 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16612 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16614 unselect : function(nodeInfo, keepExisting, suppressEvent)
16616 if(nodeInfo instanceof Array){
16617 Roo.each(this.selections, function(s) {
16618 this.unselect(s, nodeInfo);
16622 var node = this.getNode(nodeInfo);
16623 if(!node || !this.isSelected(node)){
16624 //Roo.log("not selected");
16625 return; // not selected.
16629 Roo.each(this.selections, function(s) {
16631 Roo.fly(node).removeClass(this.selectedClass);
16638 this.selections= ns;
16639 this.fireEvent("selectionchange", this, this.selections);
16643 * Gets a template node.
16644 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16645 * @return {HTMLElement} The node or null if it wasn't found
16647 getNode : function(nodeInfo){
16648 if(typeof nodeInfo == "string"){
16649 return document.getElementById(nodeInfo);
16650 }else if(typeof nodeInfo == "number"){
16651 return this.nodes[nodeInfo];
16657 * Gets a range template nodes.
16658 * @param {Number} startIndex
16659 * @param {Number} endIndex
16660 * @return {Array} An array of nodes
16662 getNodes : function(start, end){
16663 var ns = this.nodes;
16664 start = start || 0;
16665 end = typeof end == "undefined" ? ns.length - 1 : end;
16668 for(var i = start; i <= end; i++){
16672 for(var i = start; i >= end; i--){
16680 * Finds the index of the passed node
16681 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16682 * @return {Number} The index of the node or -1
16684 indexOf : function(node){
16685 node = this.getNode(node);
16686 if(typeof node.nodeIndex == "number"){
16687 return node.nodeIndex;
16689 var ns = this.nodes;
16690 for(var i = 0, len = ns.length; i < len; i++){
16701 * based on jquery fullcalendar
16705 Roo.bootstrap = Roo.bootstrap || {};
16707 * @class Roo.bootstrap.Calendar
16708 * @extends Roo.bootstrap.Component
16709 * Bootstrap Calendar class
16710 * @cfg {Boolean} loadMask (true|false) default false
16711 * @cfg {Object} header generate the user specific header of the calendar, default false
16714 * Create a new Container
16715 * @param {Object} config The config object
16720 Roo.bootstrap.Calendar = function(config){
16721 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16725 * Fires when a date is selected
16726 * @param {DatePicker} this
16727 * @param {Date} date The selected date
16731 * @event monthchange
16732 * Fires when the displayed month changes
16733 * @param {DatePicker} this
16734 * @param {Date} date The selected month
16736 'monthchange': true,
16738 * @event evententer
16739 * Fires when mouse over an event
16740 * @param {Calendar} this
16741 * @param {event} Event
16743 'evententer': true,
16745 * @event eventleave
16746 * Fires when the mouse leaves an
16747 * @param {Calendar} this
16750 'eventleave': true,
16752 * @event eventclick
16753 * Fires when the mouse click an
16754 * @param {Calendar} this
16763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16766 * @cfg {Number} startDay
16767 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16775 getAutoCreate : function(){
16778 var fc_button = function(name, corner, style, content ) {
16779 return Roo.apply({},{
16781 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16783 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16786 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16797 style : 'width:100%',
16804 cls : 'fc-header-left',
16806 fc_button('prev', 'left', 'arrow', '‹' ),
16807 fc_button('next', 'right', 'arrow', '›' ),
16808 { tag: 'span', cls: 'fc-header-space' },
16809 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16817 cls : 'fc-header-center',
16821 cls: 'fc-header-title',
16824 html : 'month / year'
16832 cls : 'fc-header-right',
16834 /* fc_button('month', 'left', '', 'month' ),
16835 fc_button('week', '', '', 'week' ),
16836 fc_button('day', 'right', '', 'day' )
16848 header = this.header;
16851 var cal_heads = function() {
16853 // fixme - handle this.
16855 for (var i =0; i < Date.dayNames.length; i++) {
16856 var d = Date.dayNames[i];
16859 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16860 html : d.substring(0,3)
16864 ret[0].cls += ' fc-first';
16865 ret[6].cls += ' fc-last';
16868 var cal_cell = function(n) {
16871 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16876 cls: 'fc-day-number',
16880 cls: 'fc-day-content',
16884 style: 'position: relative;' // height: 17px;
16896 var cal_rows = function() {
16899 for (var r = 0; r < 6; r++) {
16906 for (var i =0; i < Date.dayNames.length; i++) {
16907 var d = Date.dayNames[i];
16908 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16911 row.cn[0].cls+=' fc-first';
16912 row.cn[0].cn[0].style = 'min-height:90px';
16913 row.cn[6].cls+=' fc-last';
16917 ret[0].cls += ' fc-first';
16918 ret[4].cls += ' fc-prev-last';
16919 ret[5].cls += ' fc-last';
16926 cls: 'fc-border-separate',
16927 style : 'width:100%',
16935 cls : 'fc-first fc-last',
16953 cls : 'fc-content',
16954 style : "position: relative;",
16957 cls : 'fc-view fc-view-month fc-grid',
16958 style : 'position: relative',
16959 unselectable : 'on',
16962 cls : 'fc-event-container',
16963 style : 'position:absolute;z-index:8;top:0;left:0;'
16981 initEvents : function()
16984 throw "can not find store for calendar";
16990 style: "text-align:center",
16994 style: "background-color:white;width:50%;margin:250 auto",
16998 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17009 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17011 var size = this.el.select('.fc-content', true).first().getSize();
17012 this.maskEl.setSize(size.width, size.height);
17013 this.maskEl.enableDisplayMode("block");
17014 if(!this.loadMask){
17015 this.maskEl.hide();
17018 this.store = Roo.factory(this.store, Roo.data);
17019 this.store.on('load', this.onLoad, this);
17020 this.store.on('beforeload', this.onBeforeLoad, this);
17024 this.cells = this.el.select('.fc-day',true);
17025 //Roo.log(this.cells);
17026 this.textNodes = this.el.query('.fc-day-number');
17027 this.cells.addClassOnOver('fc-state-hover');
17029 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17030 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17031 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17032 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17034 this.on('monthchange', this.onMonthChange, this);
17036 this.update(new Date().clearTime());
17039 resize : function() {
17040 var sz = this.el.getSize();
17042 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17043 this.el.select('.fc-day-content div',true).setHeight(34);
17048 showPrevMonth : function(e){
17049 this.update(this.activeDate.add("mo", -1));
17051 showToday : function(e){
17052 this.update(new Date().clearTime());
17055 showNextMonth : function(e){
17056 this.update(this.activeDate.add("mo", 1));
17060 showPrevYear : function(){
17061 this.update(this.activeDate.add("y", -1));
17065 showNextYear : function(){
17066 this.update(this.activeDate.add("y", 1));
17071 update : function(date)
17073 var vd = this.activeDate;
17074 this.activeDate = date;
17075 // if(vd && this.el){
17076 // var t = date.getTime();
17077 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17078 // Roo.log('using add remove');
17080 // this.fireEvent('monthchange', this, date);
17082 // this.cells.removeClass("fc-state-highlight");
17083 // this.cells.each(function(c){
17084 // if(c.dateValue == t){
17085 // c.addClass("fc-state-highlight");
17086 // setTimeout(function(){
17087 // try{c.dom.firstChild.focus();}catch(e){}
17097 var days = date.getDaysInMonth();
17099 var firstOfMonth = date.getFirstDateOfMonth();
17100 var startingPos = firstOfMonth.getDay()-this.startDay;
17102 if(startingPos < this.startDay){
17106 var pm = date.add(Date.MONTH, -1);
17107 var prevStart = pm.getDaysInMonth()-startingPos;
17109 this.cells = this.el.select('.fc-day',true);
17110 this.textNodes = this.el.query('.fc-day-number');
17111 this.cells.addClassOnOver('fc-state-hover');
17113 var cells = this.cells.elements;
17114 var textEls = this.textNodes;
17116 Roo.each(cells, function(cell){
17117 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17120 days += startingPos;
17122 // convert everything to numbers so it's fast
17123 var day = 86400000;
17124 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17127 //Roo.log(prevStart);
17129 var today = new Date().clearTime().getTime();
17130 var sel = date.clearTime().getTime();
17131 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17132 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17133 var ddMatch = this.disabledDatesRE;
17134 var ddText = this.disabledDatesText;
17135 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17136 var ddaysText = this.disabledDaysText;
17137 var format = this.format;
17139 var setCellClass = function(cal, cell){
17143 //Roo.log('set Cell Class');
17145 var t = d.getTime();
17149 cell.dateValue = t;
17151 cell.className += " fc-today";
17152 cell.className += " fc-state-highlight";
17153 cell.title = cal.todayText;
17156 // disable highlight in other month..
17157 //cell.className += " fc-state-highlight";
17162 cell.className = " fc-state-disabled";
17163 cell.title = cal.minText;
17167 cell.className = " fc-state-disabled";
17168 cell.title = cal.maxText;
17172 if(ddays.indexOf(d.getDay()) != -1){
17173 cell.title = ddaysText;
17174 cell.className = " fc-state-disabled";
17177 if(ddMatch && format){
17178 var fvalue = d.dateFormat(format);
17179 if(ddMatch.test(fvalue)){
17180 cell.title = ddText.replace("%0", fvalue);
17181 cell.className = " fc-state-disabled";
17185 if (!cell.initialClassName) {
17186 cell.initialClassName = cell.dom.className;
17189 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17194 for(; i < startingPos; i++) {
17195 textEls[i].innerHTML = (++prevStart);
17196 d.setDate(d.getDate()+1);
17198 cells[i].className = "fc-past fc-other-month";
17199 setCellClass(this, cells[i]);
17204 for(; i < days; i++){
17205 intDay = i - startingPos + 1;
17206 textEls[i].innerHTML = (intDay);
17207 d.setDate(d.getDate()+1);
17209 cells[i].className = ''; // "x-date-active";
17210 setCellClass(this, cells[i]);
17214 for(; i < 42; i++) {
17215 textEls[i].innerHTML = (++extraDays);
17216 d.setDate(d.getDate()+1);
17218 cells[i].className = "fc-future fc-other-month";
17219 setCellClass(this, cells[i]);
17222 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17224 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17226 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17227 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17229 if(totalRows != 6){
17230 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17231 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17234 this.fireEvent('monthchange', this, date);
17238 if(!this.internalRender){
17239 var main = this.el.dom.firstChild;
17240 var w = main.offsetWidth;
17241 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17242 Roo.fly(main).setWidth(w);
17243 this.internalRender = true;
17244 // opera does not respect the auto grow header center column
17245 // then, after it gets a width opera refuses to recalculate
17246 // without a second pass
17247 if(Roo.isOpera && !this.secondPass){
17248 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17249 this.secondPass = true;
17250 this.update.defer(10, this, [date]);
17257 findCell : function(dt) {
17258 dt = dt.clearTime().getTime();
17260 this.cells.each(function(c){
17261 //Roo.log("check " +c.dateValue + '?=' + dt);
17262 if(c.dateValue == dt){
17272 findCells : function(ev) {
17273 var s = ev.start.clone().clearTime().getTime();
17275 var e= ev.end.clone().clearTime().getTime();
17278 this.cells.each(function(c){
17279 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17281 if(c.dateValue > e){
17284 if(c.dateValue < s){
17293 // findBestRow: function(cells)
17297 // for (var i =0 ; i < cells.length;i++) {
17298 // ret = Math.max(cells[i].rows || 0,ret);
17305 addItem : function(ev)
17307 // look for vertical location slot in
17308 var cells = this.findCells(ev);
17310 // ev.row = this.findBestRow(cells);
17312 // work out the location.
17316 for(var i =0; i < cells.length; i++) {
17318 cells[i].row = cells[0].row;
17321 cells[i].row = cells[i].row + 1;
17331 if (crow.start.getY() == cells[i].getY()) {
17333 crow.end = cells[i];
17350 cells[0].events.push(ev);
17352 this.calevents.push(ev);
17355 clearEvents: function() {
17357 if(!this.calevents){
17361 Roo.each(this.cells.elements, function(c){
17367 Roo.each(this.calevents, function(e) {
17368 Roo.each(e.els, function(el) {
17369 el.un('mouseenter' ,this.onEventEnter, this);
17370 el.un('mouseleave' ,this.onEventLeave, this);
17375 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17381 renderEvents: function()
17385 this.cells.each(function(c) {
17394 if(c.row != c.events.length){
17395 r = 4 - (4 - (c.row - c.events.length));
17398 c.events = ev.slice(0, r);
17399 c.more = ev.slice(r);
17401 if(c.more.length && c.more.length == 1){
17402 c.events.push(c.more.pop());
17405 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17409 this.cells.each(function(c) {
17411 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17414 for (var e = 0; e < c.events.length; e++){
17415 var ev = c.events[e];
17416 var rows = ev.rows;
17418 for(var i = 0; i < rows.length; i++) {
17420 // how many rows should it span..
17423 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17424 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17426 unselectable : "on",
17429 cls: 'fc-event-inner',
17433 // cls: 'fc-event-time',
17434 // html : cells.length > 1 ? '' : ev.time
17438 cls: 'fc-event-title',
17439 html : String.format('{0}', ev.title)
17446 cls: 'ui-resizable-handle ui-resizable-e',
17447 html : '  '
17454 cfg.cls += ' fc-event-start';
17456 if ((i+1) == rows.length) {
17457 cfg.cls += ' fc-event-end';
17460 var ctr = _this.el.select('.fc-event-container',true).first();
17461 var cg = ctr.createChild(cfg);
17463 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17464 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17466 var r = (c.more.length) ? 1 : 0;
17467 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17468 cg.setWidth(ebox.right - sbox.x -2);
17470 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17471 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17472 cg.on('click', _this.onEventClick, _this, ev);
17483 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17484 style : 'position: absolute',
17485 unselectable : "on",
17488 cls: 'fc-event-inner',
17492 cls: 'fc-event-title',
17500 cls: 'ui-resizable-handle ui-resizable-e',
17501 html : '  '
17507 var ctr = _this.el.select('.fc-event-container',true).first();
17508 var cg = ctr.createChild(cfg);
17510 var sbox = c.select('.fc-day-content',true).first().getBox();
17511 var ebox = c.select('.fc-day-content',true).first().getBox();
17513 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17514 cg.setWidth(ebox.right - sbox.x -2);
17516 cg.on('click', _this.onMoreEventClick, _this, c.more);
17526 onEventEnter: function (e, el,event,d) {
17527 this.fireEvent('evententer', this, el, event);
17530 onEventLeave: function (e, el,event,d) {
17531 this.fireEvent('eventleave', this, el, event);
17534 onEventClick: function (e, el,event,d) {
17535 this.fireEvent('eventclick', this, el, event);
17538 onMonthChange: function () {
17542 onMoreEventClick: function(e, el, more)
17546 this.calpopover.placement = 'right';
17547 this.calpopover.setTitle('More');
17549 this.calpopover.setContent('');
17551 var ctr = this.calpopover.el.select('.popover-content', true).first();
17553 Roo.each(more, function(m){
17555 cls : 'fc-event-hori fc-event-draggable',
17558 var cg = ctr.createChild(cfg);
17560 cg.on('click', _this.onEventClick, _this, m);
17563 this.calpopover.show(el);
17568 onLoad: function ()
17570 this.calevents = [];
17573 if(this.store.getCount() > 0){
17574 this.store.data.each(function(d){
17577 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17578 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17579 time : d.data.start_time,
17580 title : d.data.title,
17581 description : d.data.description,
17582 venue : d.data.venue
17587 this.renderEvents();
17589 if(this.calevents.length && this.loadMask){
17590 this.maskEl.hide();
17594 onBeforeLoad: function()
17596 this.clearEvents();
17598 this.maskEl.show();
17612 * @class Roo.bootstrap.Popover
17613 * @extends Roo.bootstrap.Component
17614 * Bootstrap Popover class
17615 * @cfg {String} html contents of the popover (or false to use children..)
17616 * @cfg {String} title of popover (or false to hide)
17617 * @cfg {String} placement how it is placed
17618 * @cfg {String} trigger click || hover (or false to trigger manually)
17619 * @cfg {String} over what (parent or false to trigger manually.)
17620 * @cfg {Number} delay - delay before showing
17623 * Create a new Popover
17624 * @param {Object} config The config object
17627 Roo.bootstrap.Popover = function(config){
17628 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17634 * After the popover show
17636 * @param {Roo.bootstrap.Popover} this
17641 * After the popover hide
17643 * @param {Roo.bootstrap.Popover} this
17649 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17651 title: 'Fill in a title',
17654 placement : 'right',
17655 trigger : 'hover', // hover
17661 can_build_overlaid : false,
17663 getChildContainer : function()
17665 return this.el.select('.popover-content',true).first();
17668 getAutoCreate : function(){
17671 cls : 'popover roo-dynamic',
17672 style: 'display:block',
17678 cls : 'popover-inner',
17682 cls: 'popover-title popover-header',
17686 cls : 'popover-content popover-body',
17697 setTitle: function(str)
17700 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17702 setContent: function(str)
17705 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17707 // as it get's added to the bottom of the page.
17708 onRender : function(ct, position)
17710 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17712 var cfg = Roo.apply({}, this.getAutoCreate());
17716 cfg.cls += ' ' + this.cls;
17719 cfg.style = this.style;
17721 //Roo.log("adding to ");
17722 this.el = Roo.get(document.body).createChild(cfg, position);
17723 // Roo.log(this.el);
17728 initEvents : function()
17730 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17731 this.el.enableDisplayMode('block');
17733 if (this.over === false) {
17736 if (this.triggers === false) {
17739 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17740 var triggers = this.trigger ? this.trigger.split(' ') : [];
17741 Roo.each(triggers, function(trigger) {
17743 if (trigger == 'click') {
17744 on_el.on('click', this.toggle, this);
17745 } else if (trigger != 'manual') {
17746 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17747 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17749 on_el.on(eventIn ,this.enter, this);
17750 on_el.on(eventOut, this.leave, this);
17761 toggle : function () {
17762 this.hoverState == 'in' ? this.leave() : this.enter();
17765 enter : function () {
17767 clearTimeout(this.timeout);
17769 this.hoverState = 'in';
17771 if (!this.delay || !this.delay.show) {
17776 this.timeout = setTimeout(function () {
17777 if (_t.hoverState == 'in') {
17780 }, this.delay.show)
17783 leave : function() {
17784 clearTimeout(this.timeout);
17786 this.hoverState = 'out';
17788 if (!this.delay || !this.delay.hide) {
17793 this.timeout = setTimeout(function () {
17794 if (_t.hoverState == 'out') {
17797 }, this.delay.hide)
17800 show : function (on_el)
17803 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17807 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17808 if (this.html !== false) {
17809 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17811 this.el.removeClass([
17812 'fade','top','bottom', 'left', 'right','in',
17813 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17815 if (!this.title.length) {
17816 this.el.select('.popover-title',true).hide();
17819 var placement = typeof this.placement == 'function' ?
17820 this.placement.call(this, this.el, on_el) :
17823 var autoToken = /\s?auto?\s?/i;
17824 var autoPlace = autoToken.test(placement);
17826 placement = placement.replace(autoToken, '') || 'top';
17830 //this.el.setXY([0,0]);
17832 this.el.dom.style.display='block';
17833 this.el.addClass(placement);
17835 //this.el.appendTo(on_el);
17837 var p = this.getPosition();
17838 var box = this.el.getBox();
17843 var align = Roo.bootstrap.Popover.alignment[placement];
17846 this.el.alignTo(on_el, align[0],align[1]);
17847 //var arrow = this.el.select('.arrow',true).first();
17848 //arrow.set(align[2],
17850 this.el.addClass('in');
17853 if (this.el.hasClass('fade')) {
17857 this.hoverState = 'in';
17859 this.fireEvent('show', this);
17864 this.el.setXY([0,0]);
17865 this.el.removeClass('in');
17867 this.hoverState = null;
17869 this.fireEvent('hide', this);
17874 Roo.bootstrap.Popover.alignment = {
17875 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17876 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17877 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17878 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17889 * @class Roo.bootstrap.Progress
17890 * @extends Roo.bootstrap.Component
17891 * Bootstrap Progress class
17892 * @cfg {Boolean} striped striped of the progress bar
17893 * @cfg {Boolean} active animated of the progress bar
17897 * Create a new Progress
17898 * @param {Object} config The config object
17901 Roo.bootstrap.Progress = function(config){
17902 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17905 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17910 getAutoCreate : function(){
17918 cfg.cls += ' progress-striped';
17922 cfg.cls += ' active';
17941 * @class Roo.bootstrap.ProgressBar
17942 * @extends Roo.bootstrap.Component
17943 * Bootstrap ProgressBar class
17944 * @cfg {Number} aria_valuenow aria-value now
17945 * @cfg {Number} aria_valuemin aria-value min
17946 * @cfg {Number} aria_valuemax aria-value max
17947 * @cfg {String} label label for the progress bar
17948 * @cfg {String} panel (success | info | warning | danger )
17949 * @cfg {String} role role of the progress bar
17950 * @cfg {String} sr_only text
17954 * Create a new ProgressBar
17955 * @param {Object} config The config object
17958 Roo.bootstrap.ProgressBar = function(config){
17959 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17962 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17966 aria_valuemax : 100,
17972 getAutoCreate : function()
17977 cls: 'progress-bar',
17978 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17990 cfg.role = this.role;
17993 if(this.aria_valuenow){
17994 cfg['aria-valuenow'] = this.aria_valuenow;
17997 if(this.aria_valuemin){
17998 cfg['aria-valuemin'] = this.aria_valuemin;
18001 if(this.aria_valuemax){
18002 cfg['aria-valuemax'] = this.aria_valuemax;
18005 if(this.label && !this.sr_only){
18006 cfg.html = this.label;
18010 cfg.cls += ' progress-bar-' + this.panel;
18016 update : function(aria_valuenow)
18018 this.aria_valuenow = aria_valuenow;
18020 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18035 * @class Roo.bootstrap.TabGroup
18036 * @extends Roo.bootstrap.Column
18037 * Bootstrap Column class
18038 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18039 * @cfg {Boolean} carousel true to make the group behave like a carousel
18040 * @cfg {Boolean} bullets show bullets for the panels
18041 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18042 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18043 * @cfg {Boolean} showarrow (true|false) show arrow default true
18046 * Create a new TabGroup
18047 * @param {Object} config The config object
18050 Roo.bootstrap.TabGroup = function(config){
18051 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18053 this.navId = Roo.id();
18056 Roo.bootstrap.TabGroup.register(this);
18060 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18063 transition : false,
18068 slideOnTouch : false,
18071 getAutoCreate : function()
18073 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18075 cfg.cls += ' tab-content';
18077 if (this.carousel) {
18078 cfg.cls += ' carousel slide';
18081 cls : 'carousel-inner',
18085 if(this.bullets && !Roo.isTouch){
18088 cls : 'carousel-bullets',
18092 if(this.bullets_cls){
18093 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18100 cfg.cn[0].cn.push(bullets);
18103 if(this.showarrow){
18104 cfg.cn[0].cn.push({
18106 class : 'carousel-arrow',
18110 class : 'carousel-prev',
18114 class : 'fa fa-chevron-left'
18120 class : 'carousel-next',
18124 class : 'fa fa-chevron-right'
18137 initEvents: function()
18139 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18140 // this.el.on("touchstart", this.onTouchStart, this);
18143 if(this.autoslide){
18146 this.slideFn = window.setInterval(function() {
18147 _this.showPanelNext();
18151 if(this.showarrow){
18152 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18153 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18159 // onTouchStart : function(e, el, o)
18161 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18165 // this.showPanelNext();
18169 getChildContainer : function()
18171 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18175 * register a Navigation item
18176 * @param {Roo.bootstrap.NavItem} the navitem to add
18178 register : function(item)
18180 this.tabs.push( item);
18181 item.navId = this.navId; // not really needed..
18186 getActivePanel : function()
18189 Roo.each(this.tabs, function(t) {
18199 getPanelByName : function(n)
18202 Roo.each(this.tabs, function(t) {
18203 if (t.tabId == n) {
18211 indexOfPanel : function(p)
18214 Roo.each(this.tabs, function(t,i) {
18215 if (t.tabId == p.tabId) {
18224 * show a specific panel
18225 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18226 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18228 showPanel : function (pan)
18230 if(this.transition || typeof(pan) == 'undefined'){
18231 Roo.log("waiting for the transitionend");
18235 if (typeof(pan) == 'number') {
18236 pan = this.tabs[pan];
18239 if (typeof(pan) == 'string') {
18240 pan = this.getPanelByName(pan);
18243 var cur = this.getActivePanel();
18246 Roo.log('pan or acitve pan is undefined');
18250 if (pan.tabId == this.getActivePanel().tabId) {
18254 if (false === cur.fireEvent('beforedeactivate')) {
18258 if(this.bullets > 0 && !Roo.isTouch){
18259 this.setActiveBullet(this.indexOfPanel(pan));
18262 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18264 this.transition = true;
18265 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18266 var lr = dir == 'next' ? 'left' : 'right';
18267 pan.el.addClass(dir); // or prev
18268 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18269 cur.el.addClass(lr); // or right
18270 pan.el.addClass(lr);
18273 cur.el.on('transitionend', function() {
18274 Roo.log("trans end?");
18276 pan.el.removeClass([lr,dir]);
18277 pan.setActive(true);
18279 cur.el.removeClass([lr]);
18280 cur.setActive(false);
18282 _this.transition = false;
18284 }, this, { single: true } );
18289 cur.setActive(false);
18290 pan.setActive(true);
18295 showPanelNext : function()
18297 var i = this.indexOfPanel(this.getActivePanel());
18299 if (i >= this.tabs.length - 1 && !this.autoslide) {
18303 if (i >= this.tabs.length - 1 && this.autoslide) {
18307 this.showPanel(this.tabs[i+1]);
18310 showPanelPrev : function()
18312 var i = this.indexOfPanel(this.getActivePanel());
18314 if (i < 1 && !this.autoslide) {
18318 if (i < 1 && this.autoslide) {
18319 i = this.tabs.length;
18322 this.showPanel(this.tabs[i-1]);
18326 addBullet: function()
18328 if(!this.bullets || Roo.isTouch){
18331 var ctr = this.el.select('.carousel-bullets',true).first();
18332 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18333 var bullet = ctr.createChild({
18334 cls : 'bullet bullet-' + i
18335 },ctr.dom.lastChild);
18340 bullet.on('click', (function(e, el, o, ii, t){
18342 e.preventDefault();
18344 this.showPanel(ii);
18346 if(this.autoslide && this.slideFn){
18347 clearInterval(this.slideFn);
18348 this.slideFn = window.setInterval(function() {
18349 _this.showPanelNext();
18353 }).createDelegate(this, [i, bullet], true));
18358 setActiveBullet : function(i)
18364 Roo.each(this.el.select('.bullet', true).elements, function(el){
18365 el.removeClass('selected');
18368 var bullet = this.el.select('.bullet-' + i, true).first();
18374 bullet.addClass('selected');
18385 Roo.apply(Roo.bootstrap.TabGroup, {
18389 * register a Navigation Group
18390 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18392 register : function(navgrp)
18394 this.groups[navgrp.navId] = navgrp;
18398 * fetch a Navigation Group based on the navigation ID
18399 * if one does not exist , it will get created.
18400 * @param {string} the navgroup to add
18401 * @returns {Roo.bootstrap.NavGroup} the navgroup
18403 get: function(navId) {
18404 if (typeof(this.groups[navId]) == 'undefined') {
18405 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18407 return this.groups[navId] ;
18422 * @class Roo.bootstrap.TabPanel
18423 * @extends Roo.bootstrap.Component
18424 * Bootstrap TabPanel class
18425 * @cfg {Boolean} active panel active
18426 * @cfg {String} html panel content
18427 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18428 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18429 * @cfg {String} href click to link..
18433 * Create a new TabPanel
18434 * @param {Object} config The config object
18437 Roo.bootstrap.TabPanel = function(config){
18438 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18442 * Fires when the active status changes
18443 * @param {Roo.bootstrap.TabPanel} this
18444 * @param {Boolean} state the new state
18449 * @event beforedeactivate
18450 * Fires before a tab is de-activated - can be used to do validation on a form.
18451 * @param {Roo.bootstrap.TabPanel} this
18452 * @return {Boolean} false if there is an error
18455 'beforedeactivate': true
18458 this.tabId = this.tabId || Roo.id();
18462 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18470 getAutoCreate : function(){
18473 // item is needed for carousel - not sure if it has any effect otherwise
18474 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18475 html: this.html || ''
18479 cfg.cls += ' active';
18483 cfg.tabId = this.tabId;
18490 initEvents: function()
18492 var p = this.parent();
18494 this.navId = this.navId || p.navId;
18496 if (typeof(this.navId) != 'undefined') {
18497 // not really needed.. but just in case.. parent should be a NavGroup.
18498 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18502 var i = tg.tabs.length - 1;
18504 if(this.active && tg.bullets > 0 && i < tg.bullets){
18505 tg.setActiveBullet(i);
18509 this.el.on('click', this.onClick, this);
18512 this.el.on("touchstart", this.onTouchStart, this);
18513 this.el.on("touchmove", this.onTouchMove, this);
18514 this.el.on("touchend", this.onTouchEnd, this);
18519 onRender : function(ct, position)
18521 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18524 setActive : function(state)
18526 Roo.log("panel - set active " + this.tabId + "=" + state);
18528 this.active = state;
18530 this.el.removeClass('active');
18532 } else if (!this.el.hasClass('active')) {
18533 this.el.addClass('active');
18536 this.fireEvent('changed', this, state);
18539 onClick : function(e)
18541 e.preventDefault();
18543 if(!this.href.length){
18547 window.location.href = this.href;
18556 onTouchStart : function(e)
18558 this.swiping = false;
18560 this.startX = e.browserEvent.touches[0].clientX;
18561 this.startY = e.browserEvent.touches[0].clientY;
18564 onTouchMove : function(e)
18566 this.swiping = true;
18568 this.endX = e.browserEvent.touches[0].clientX;
18569 this.endY = e.browserEvent.touches[0].clientY;
18572 onTouchEnd : function(e)
18579 var tabGroup = this.parent();
18581 if(this.endX > this.startX){ // swiping right
18582 tabGroup.showPanelPrev();
18586 if(this.startX > this.endX){ // swiping left
18587 tabGroup.showPanelNext();
18606 * @class Roo.bootstrap.DateField
18607 * @extends Roo.bootstrap.Input
18608 * Bootstrap DateField class
18609 * @cfg {Number} weekStart default 0
18610 * @cfg {String} viewMode default empty, (months|years)
18611 * @cfg {String} minViewMode default empty, (months|years)
18612 * @cfg {Number} startDate default -Infinity
18613 * @cfg {Number} endDate default Infinity
18614 * @cfg {Boolean} todayHighlight default false
18615 * @cfg {Boolean} todayBtn default false
18616 * @cfg {Boolean} calendarWeeks default false
18617 * @cfg {Object} daysOfWeekDisabled default empty
18618 * @cfg {Boolean} singleMode default false (true | false)
18620 * @cfg {Boolean} keyboardNavigation default true
18621 * @cfg {String} language default en
18624 * Create a new DateField
18625 * @param {Object} config The config object
18628 Roo.bootstrap.DateField = function(config){
18629 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18633 * Fires when this field show.
18634 * @param {Roo.bootstrap.DateField} this
18635 * @param {Mixed} date The date value
18640 * Fires when this field hide.
18641 * @param {Roo.bootstrap.DateField} this
18642 * @param {Mixed} date The date value
18647 * Fires when select a date.
18648 * @param {Roo.bootstrap.DateField} this
18649 * @param {Mixed} date The date value
18653 * @event beforeselect
18654 * Fires when before select a date.
18655 * @param {Roo.bootstrap.DateField} this
18656 * @param {Mixed} date The date value
18658 beforeselect : true
18662 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18665 * @cfg {String} format
18666 * The default date format string which can be overriden for localization support. The format must be
18667 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18671 * @cfg {String} altFormats
18672 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18673 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18675 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18683 todayHighlight : false,
18689 keyboardNavigation: true,
18691 calendarWeeks: false,
18693 startDate: -Infinity,
18697 daysOfWeekDisabled: [],
18701 singleMode : false,
18703 UTCDate: function()
18705 return new Date(Date.UTC.apply(Date, arguments));
18708 UTCToday: function()
18710 var today = new Date();
18711 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18714 getDate: function() {
18715 var d = this.getUTCDate();
18716 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18719 getUTCDate: function() {
18723 setDate: function(d) {
18724 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18727 setUTCDate: function(d) {
18729 this.setValue(this.formatDate(this.date));
18732 onRender: function(ct, position)
18735 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18737 this.language = this.language || 'en';
18738 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18739 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18741 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18742 this.format = this.format || 'm/d/y';
18743 this.isInline = false;
18744 this.isInput = true;
18745 this.component = this.el.select('.add-on', true).first() || false;
18746 this.component = (this.component && this.component.length === 0) ? false : this.component;
18747 this.hasInput = this.component && this.inputEl().length;
18749 if (typeof(this.minViewMode === 'string')) {
18750 switch (this.minViewMode) {
18752 this.minViewMode = 1;
18755 this.minViewMode = 2;
18758 this.minViewMode = 0;
18763 if (typeof(this.viewMode === 'string')) {
18764 switch (this.viewMode) {
18777 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18779 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18781 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18783 this.picker().on('mousedown', this.onMousedown, this);
18784 this.picker().on('click', this.onClick, this);
18786 this.picker().addClass('datepicker-dropdown');
18788 this.startViewMode = this.viewMode;
18790 if(this.singleMode){
18791 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18792 v.setVisibilityMode(Roo.Element.DISPLAY);
18796 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18797 v.setStyle('width', '189px');
18801 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18802 if(!this.calendarWeeks){
18807 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18808 v.attr('colspan', function(i, val){
18809 return parseInt(val) + 1;
18814 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18816 this.setStartDate(this.startDate);
18817 this.setEndDate(this.endDate);
18819 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18826 if(this.isInline) {
18831 picker : function()
18833 return this.pickerEl;
18834 // return this.el.select('.datepicker', true).first();
18837 fillDow: function()
18839 var dowCnt = this.weekStart;
18848 if(this.calendarWeeks){
18856 while (dowCnt < this.weekStart + 7) {
18860 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18864 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18867 fillMonths: function()
18870 var months = this.picker().select('>.datepicker-months td', true).first();
18872 months.dom.innerHTML = '';
18878 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18881 months.createChild(month);
18888 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;
18890 if (this.date < this.startDate) {
18891 this.viewDate = new Date(this.startDate);
18892 } else if (this.date > this.endDate) {
18893 this.viewDate = new Date(this.endDate);
18895 this.viewDate = new Date(this.date);
18903 var d = new Date(this.viewDate),
18904 year = d.getUTCFullYear(),
18905 month = d.getUTCMonth(),
18906 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18907 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18908 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18909 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18910 currentDate = this.date && this.date.valueOf(),
18911 today = this.UTCToday();
18913 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18915 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18917 // this.picker.select('>tfoot th.today').
18918 // .text(dates[this.language].today)
18919 // .toggle(this.todayBtn !== false);
18921 this.updateNavArrows();
18924 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18926 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18928 prevMonth.setUTCDate(day);
18930 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18932 var nextMonth = new Date(prevMonth);
18934 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18936 nextMonth = nextMonth.valueOf();
18938 var fillMonths = false;
18940 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18942 while(prevMonth.valueOf() <= nextMonth) {
18945 if (prevMonth.getUTCDay() === this.weekStart) {
18947 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18955 if(this.calendarWeeks){
18956 // ISO 8601: First week contains first thursday.
18957 // ISO also states week starts on Monday, but we can be more abstract here.
18959 // Start of current week: based on weekstart/current date
18960 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18961 // Thursday of this week
18962 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18963 // First Thursday of year, year from thursday
18964 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18965 // Calendar week: ms between thursdays, div ms per day, div 7 days
18966 calWeek = (th - yth) / 864e5 / 7 + 1;
18968 fillMonths.cn.push({
18976 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18978 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18981 if (this.todayHighlight &&
18982 prevMonth.getUTCFullYear() == today.getFullYear() &&
18983 prevMonth.getUTCMonth() == today.getMonth() &&
18984 prevMonth.getUTCDate() == today.getDate()) {
18985 clsName += ' today';
18988 if (currentDate && prevMonth.valueOf() === currentDate) {
18989 clsName += ' active';
18992 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18993 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18994 clsName += ' disabled';
18997 fillMonths.cn.push({
18999 cls: 'day ' + clsName,
19000 html: prevMonth.getDate()
19003 prevMonth.setDate(prevMonth.getDate()+1);
19006 var currentYear = this.date && this.date.getUTCFullYear();
19007 var currentMonth = this.date && this.date.getUTCMonth();
19009 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19011 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19012 v.removeClass('active');
19014 if(currentYear === year && k === currentMonth){
19015 v.addClass('active');
19018 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19019 v.addClass('disabled');
19025 year = parseInt(year/10, 10) * 10;
19027 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19029 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19032 for (var i = -1; i < 11; i++) {
19033 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19035 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19043 showMode: function(dir)
19046 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19049 Roo.each(this.picker().select('>div',true).elements, function(v){
19050 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19053 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19058 if(this.isInline) {
19062 this.picker().removeClass(['bottom', 'top']);
19064 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19066 * place to the top of element!
19070 this.picker().addClass('top');
19071 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19076 this.picker().addClass('bottom');
19078 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19081 parseDate : function(value)
19083 if(!value || value instanceof Date){
19086 var v = Date.parseDate(value, this.format);
19087 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19088 v = Date.parseDate(value, 'Y-m-d');
19090 if(!v && this.altFormats){
19091 if(!this.altFormatsArray){
19092 this.altFormatsArray = this.altFormats.split("|");
19094 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19095 v = Date.parseDate(value, this.altFormatsArray[i]);
19101 formatDate : function(date, fmt)
19103 return (!date || !(date instanceof Date)) ?
19104 date : date.dateFormat(fmt || this.format);
19107 onFocus : function()
19109 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19113 onBlur : function()
19115 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19117 var d = this.inputEl().getValue();
19124 showPopup : function()
19126 this.picker().show();
19130 this.fireEvent('showpopup', this, this.date);
19133 hidePopup : function()
19135 if(this.isInline) {
19138 this.picker().hide();
19139 this.viewMode = this.startViewMode;
19142 this.fireEvent('hidepopup', this, this.date);
19146 onMousedown: function(e)
19148 e.stopPropagation();
19149 e.preventDefault();
19154 Roo.bootstrap.DateField.superclass.keyup.call(this);
19158 setValue: function(v)
19160 if(this.fireEvent('beforeselect', this, v) !== false){
19161 var d = new Date(this.parseDate(v) ).clearTime();
19163 if(isNaN(d.getTime())){
19164 this.date = this.viewDate = '';
19165 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19169 v = this.formatDate(d);
19171 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19173 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19177 this.fireEvent('select', this, this.date);
19181 getValue: function()
19183 return this.formatDate(this.date);
19186 fireKey: function(e)
19188 if (!this.picker().isVisible()){
19189 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19195 var dateChanged = false,
19197 newDate, newViewDate;
19202 e.preventDefault();
19206 if (!this.keyboardNavigation) {
19209 dir = e.keyCode == 37 ? -1 : 1;
19212 newDate = this.moveYear(this.date, dir);
19213 newViewDate = this.moveYear(this.viewDate, dir);
19214 } else if (e.shiftKey){
19215 newDate = this.moveMonth(this.date, dir);
19216 newViewDate = this.moveMonth(this.viewDate, dir);
19218 newDate = new Date(this.date);
19219 newDate.setUTCDate(this.date.getUTCDate() + dir);
19220 newViewDate = new Date(this.viewDate);
19221 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19223 if (this.dateWithinRange(newDate)){
19224 this.date = newDate;
19225 this.viewDate = newViewDate;
19226 this.setValue(this.formatDate(this.date));
19228 e.preventDefault();
19229 dateChanged = true;
19234 if (!this.keyboardNavigation) {
19237 dir = e.keyCode == 38 ? -1 : 1;
19239 newDate = this.moveYear(this.date, dir);
19240 newViewDate = this.moveYear(this.viewDate, dir);
19241 } else if (e.shiftKey){
19242 newDate = this.moveMonth(this.date, dir);
19243 newViewDate = this.moveMonth(this.viewDate, dir);
19245 newDate = new Date(this.date);
19246 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19247 newViewDate = new Date(this.viewDate);
19248 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19250 if (this.dateWithinRange(newDate)){
19251 this.date = newDate;
19252 this.viewDate = newViewDate;
19253 this.setValue(this.formatDate(this.date));
19255 e.preventDefault();
19256 dateChanged = true;
19260 this.setValue(this.formatDate(this.date));
19262 e.preventDefault();
19265 this.setValue(this.formatDate(this.date));
19279 onClick: function(e)
19281 e.stopPropagation();
19282 e.preventDefault();
19284 var target = e.getTarget();
19286 if(target.nodeName.toLowerCase() === 'i'){
19287 target = Roo.get(target).dom.parentNode;
19290 var nodeName = target.nodeName;
19291 var className = target.className;
19292 var html = target.innerHTML;
19293 //Roo.log(nodeName);
19295 switch(nodeName.toLowerCase()) {
19297 switch(className) {
19303 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19304 switch(this.viewMode){
19306 this.viewDate = this.moveMonth(this.viewDate, dir);
19310 this.viewDate = this.moveYear(this.viewDate, dir);
19316 var date = new Date();
19317 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19319 this.setValue(this.formatDate(this.date));
19326 if (className.indexOf('disabled') < 0) {
19327 this.viewDate.setUTCDate(1);
19328 if (className.indexOf('month') > -1) {
19329 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19331 var year = parseInt(html, 10) || 0;
19332 this.viewDate.setUTCFullYear(year);
19336 if(this.singleMode){
19337 this.setValue(this.formatDate(this.viewDate));
19348 //Roo.log(className);
19349 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19350 var day = parseInt(html, 10) || 1;
19351 var year = this.viewDate.getUTCFullYear(),
19352 month = this.viewDate.getUTCMonth();
19354 if (className.indexOf('old') > -1) {
19361 } else if (className.indexOf('new') > -1) {
19369 //Roo.log([year,month,day]);
19370 this.date = this.UTCDate(year, month, day,0,0,0,0);
19371 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19373 //Roo.log(this.formatDate(this.date));
19374 this.setValue(this.formatDate(this.date));
19381 setStartDate: function(startDate)
19383 this.startDate = startDate || -Infinity;
19384 if (this.startDate !== -Infinity) {
19385 this.startDate = this.parseDate(this.startDate);
19388 this.updateNavArrows();
19391 setEndDate: function(endDate)
19393 this.endDate = endDate || Infinity;
19394 if (this.endDate !== Infinity) {
19395 this.endDate = this.parseDate(this.endDate);
19398 this.updateNavArrows();
19401 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19403 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19404 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19405 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19407 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19408 return parseInt(d, 10);
19411 this.updateNavArrows();
19414 updateNavArrows: function()
19416 if(this.singleMode){
19420 var d = new Date(this.viewDate),
19421 year = d.getUTCFullYear(),
19422 month = d.getUTCMonth();
19424 Roo.each(this.picker().select('.prev', true).elements, function(v){
19426 switch (this.viewMode) {
19429 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19435 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19442 Roo.each(this.picker().select('.next', true).elements, function(v){
19444 switch (this.viewMode) {
19447 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19453 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19461 moveMonth: function(date, dir)
19466 var new_date = new Date(date.valueOf()),
19467 day = new_date.getUTCDate(),
19468 month = new_date.getUTCMonth(),
19469 mag = Math.abs(dir),
19471 dir = dir > 0 ? 1 : -1;
19474 // If going back one month, make sure month is not current month
19475 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19477 return new_date.getUTCMonth() == month;
19479 // If going forward one month, make sure month is as expected
19480 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19482 return new_date.getUTCMonth() != new_month;
19484 new_month = month + dir;
19485 new_date.setUTCMonth(new_month);
19486 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19487 if (new_month < 0 || new_month > 11) {
19488 new_month = (new_month + 12) % 12;
19491 // For magnitudes >1, move one month at a time...
19492 for (var i=0; i<mag; i++) {
19493 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19494 new_date = this.moveMonth(new_date, dir);
19496 // ...then reset the day, keeping it in the new month
19497 new_month = new_date.getUTCMonth();
19498 new_date.setUTCDate(day);
19500 return new_month != new_date.getUTCMonth();
19503 // Common date-resetting loop -- if date is beyond end of month, make it
19506 new_date.setUTCDate(--day);
19507 new_date.setUTCMonth(new_month);
19512 moveYear: function(date, dir)
19514 return this.moveMonth(date, dir*12);
19517 dateWithinRange: function(date)
19519 return date >= this.startDate && date <= this.endDate;
19525 this.picker().remove();
19528 validateValue : function(value)
19530 if(this.getVisibilityEl().hasClass('hidden')){
19534 if(value.length < 1) {
19535 if(this.allowBlank){
19541 if(value.length < this.minLength){
19544 if(value.length > this.maxLength){
19548 var vt = Roo.form.VTypes;
19549 if(!vt[this.vtype](value, this)){
19553 if(typeof this.validator == "function"){
19554 var msg = this.validator(value);
19560 if(this.regex && !this.regex.test(value)){
19564 if(typeof(this.parseDate(value)) == 'undefined'){
19568 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19572 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19582 this.date = this.viewDate = '';
19584 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19589 Roo.apply(Roo.bootstrap.DateField, {
19600 html: '<i class="fa fa-arrow-left"/>'
19610 html: '<i class="fa fa-arrow-right"/>'
19652 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19653 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19654 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19655 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19656 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19669 navFnc: 'FullYear',
19674 navFnc: 'FullYear',
19679 Roo.apply(Roo.bootstrap.DateField, {
19683 cls: 'datepicker dropdown-menu roo-dynamic',
19687 cls: 'datepicker-days',
19691 cls: 'table-condensed',
19693 Roo.bootstrap.DateField.head,
19697 Roo.bootstrap.DateField.footer
19704 cls: 'datepicker-months',
19708 cls: 'table-condensed',
19710 Roo.bootstrap.DateField.head,
19711 Roo.bootstrap.DateField.content,
19712 Roo.bootstrap.DateField.footer
19719 cls: 'datepicker-years',
19723 cls: 'table-condensed',
19725 Roo.bootstrap.DateField.head,
19726 Roo.bootstrap.DateField.content,
19727 Roo.bootstrap.DateField.footer
19746 * @class Roo.bootstrap.TimeField
19747 * @extends Roo.bootstrap.Input
19748 * Bootstrap DateField class
19752 * Create a new TimeField
19753 * @param {Object} config The config object
19756 Roo.bootstrap.TimeField = function(config){
19757 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19761 * Fires when this field show.
19762 * @param {Roo.bootstrap.DateField} thisthis
19763 * @param {Mixed} date The date value
19768 * Fires when this field hide.
19769 * @param {Roo.bootstrap.DateField} this
19770 * @param {Mixed} date The date value
19775 * Fires when select a date.
19776 * @param {Roo.bootstrap.DateField} this
19777 * @param {Mixed} date The date value
19783 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19786 * @cfg {String} format
19787 * The default time format string which can be overriden for localization support. The format must be
19788 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19792 onRender: function(ct, position)
19795 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19797 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19799 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19801 this.pop = this.picker().select('>.datepicker-time',true).first();
19802 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19804 this.picker().on('mousedown', this.onMousedown, this);
19805 this.picker().on('click', this.onClick, this);
19807 this.picker().addClass('datepicker-dropdown');
19812 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19813 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19814 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19815 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19816 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19817 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19821 fireKey: function(e){
19822 if (!this.picker().isVisible()){
19823 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19829 e.preventDefault();
19837 this.onTogglePeriod();
19840 this.onIncrementMinutes();
19843 this.onDecrementMinutes();
19852 onClick: function(e) {
19853 e.stopPropagation();
19854 e.preventDefault();
19857 picker : function()
19859 return this.el.select('.datepicker', true).first();
19862 fillTime: function()
19864 var time = this.pop.select('tbody', true).first();
19866 time.dom.innerHTML = '';
19881 cls: 'hours-up glyphicon glyphicon-chevron-up'
19901 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19922 cls: 'timepicker-hour',
19937 cls: 'timepicker-minute',
19952 cls: 'btn btn-primary period',
19974 cls: 'hours-down glyphicon glyphicon-chevron-down'
19994 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20012 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20019 var hours = this.time.getHours();
20020 var minutes = this.time.getMinutes();
20033 hours = hours - 12;
20037 hours = '0' + hours;
20041 minutes = '0' + minutes;
20044 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20045 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20046 this.pop.select('button', true).first().dom.innerHTML = period;
20052 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20054 var cls = ['bottom'];
20056 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20063 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20068 this.picker().addClass(cls.join('-'));
20072 Roo.each(cls, function(c){
20074 _this.picker().setTop(_this.inputEl().getHeight());
20078 _this.picker().setTop(0 - _this.picker().getHeight());
20083 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20087 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20094 onFocus : function()
20096 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20100 onBlur : function()
20102 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20108 this.picker().show();
20113 this.fireEvent('show', this, this.date);
20118 this.picker().hide();
20121 this.fireEvent('hide', this, this.date);
20124 setTime : function()
20127 this.setValue(this.time.format(this.format));
20129 this.fireEvent('select', this, this.date);
20134 onMousedown: function(e){
20135 e.stopPropagation();
20136 e.preventDefault();
20139 onIncrementHours: function()
20141 Roo.log('onIncrementHours');
20142 this.time = this.time.add(Date.HOUR, 1);
20147 onDecrementHours: function()
20149 Roo.log('onDecrementHours');
20150 this.time = this.time.add(Date.HOUR, -1);
20154 onIncrementMinutes: function()
20156 Roo.log('onIncrementMinutes');
20157 this.time = this.time.add(Date.MINUTE, 1);
20161 onDecrementMinutes: function()
20163 Roo.log('onDecrementMinutes');
20164 this.time = this.time.add(Date.MINUTE, -1);
20168 onTogglePeriod: function()
20170 Roo.log('onTogglePeriod');
20171 this.time = this.time.add(Date.HOUR, 12);
20178 Roo.apply(Roo.bootstrap.TimeField, {
20208 cls: 'btn btn-info ok',
20220 Roo.apply(Roo.bootstrap.TimeField, {
20224 cls: 'datepicker dropdown-menu',
20228 cls: 'datepicker-time',
20232 cls: 'table-condensed',
20234 Roo.bootstrap.TimeField.content,
20235 Roo.bootstrap.TimeField.footer
20254 * @class Roo.bootstrap.MonthField
20255 * @extends Roo.bootstrap.Input
20256 * Bootstrap MonthField class
20258 * @cfg {String} language default en
20261 * Create a new MonthField
20262 * @param {Object} config The config object
20265 Roo.bootstrap.MonthField = function(config){
20266 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20271 * Fires when this field show.
20272 * @param {Roo.bootstrap.MonthField} this
20273 * @param {Mixed} date The date value
20278 * Fires when this field hide.
20279 * @param {Roo.bootstrap.MonthField} this
20280 * @param {Mixed} date The date value
20285 * Fires when select a date.
20286 * @param {Roo.bootstrap.MonthField} this
20287 * @param {String} oldvalue The old value
20288 * @param {String} newvalue The new value
20294 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20296 onRender: function(ct, position)
20299 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20301 this.language = this.language || 'en';
20302 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20303 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20305 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20306 this.isInline = false;
20307 this.isInput = true;
20308 this.component = this.el.select('.add-on', true).first() || false;
20309 this.component = (this.component && this.component.length === 0) ? false : this.component;
20310 this.hasInput = this.component && this.inputEL().length;
20312 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20314 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20316 this.picker().on('mousedown', this.onMousedown, this);
20317 this.picker().on('click', this.onClick, this);
20319 this.picker().addClass('datepicker-dropdown');
20321 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20322 v.setStyle('width', '189px');
20329 if(this.isInline) {
20335 setValue: function(v, suppressEvent)
20337 var o = this.getValue();
20339 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20343 if(suppressEvent !== true){
20344 this.fireEvent('select', this, o, v);
20349 getValue: function()
20354 onClick: function(e)
20356 e.stopPropagation();
20357 e.preventDefault();
20359 var target = e.getTarget();
20361 if(target.nodeName.toLowerCase() === 'i'){
20362 target = Roo.get(target).dom.parentNode;
20365 var nodeName = target.nodeName;
20366 var className = target.className;
20367 var html = target.innerHTML;
20369 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20373 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20375 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20381 picker : function()
20383 return this.pickerEl;
20386 fillMonths: function()
20389 var months = this.picker().select('>.datepicker-months td', true).first();
20391 months.dom.innerHTML = '';
20397 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20400 months.createChild(month);
20409 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20410 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20413 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20414 e.removeClass('active');
20416 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20417 e.addClass('active');
20424 if(this.isInline) {
20428 this.picker().removeClass(['bottom', 'top']);
20430 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20432 * place to the top of element!
20436 this.picker().addClass('top');
20437 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20442 this.picker().addClass('bottom');
20444 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20447 onFocus : function()
20449 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20453 onBlur : function()
20455 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20457 var d = this.inputEl().getValue();
20466 this.picker().show();
20467 this.picker().select('>.datepicker-months', true).first().show();
20471 this.fireEvent('show', this, this.date);
20476 if(this.isInline) {
20479 this.picker().hide();
20480 this.fireEvent('hide', this, this.date);
20484 onMousedown: function(e)
20486 e.stopPropagation();
20487 e.preventDefault();
20492 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20496 fireKey: function(e)
20498 if (!this.picker().isVisible()){
20499 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20510 e.preventDefault();
20514 dir = e.keyCode == 37 ? -1 : 1;
20516 this.vIndex = this.vIndex + dir;
20518 if(this.vIndex < 0){
20522 if(this.vIndex > 11){
20526 if(isNaN(this.vIndex)){
20530 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20536 dir = e.keyCode == 38 ? -1 : 1;
20538 this.vIndex = this.vIndex + dir * 4;
20540 if(this.vIndex < 0){
20544 if(this.vIndex > 11){
20548 if(isNaN(this.vIndex)){
20552 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20557 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20558 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20562 e.preventDefault();
20565 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20566 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582 this.picker().remove();
20587 Roo.apply(Roo.bootstrap.MonthField, {
20606 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20607 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20612 Roo.apply(Roo.bootstrap.MonthField, {
20616 cls: 'datepicker dropdown-menu roo-dynamic',
20620 cls: 'datepicker-months',
20624 cls: 'table-condensed',
20626 Roo.bootstrap.DateField.content
20646 * @class Roo.bootstrap.CheckBox
20647 * @extends Roo.bootstrap.Input
20648 * Bootstrap CheckBox class
20650 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20651 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20652 * @cfg {String} boxLabel The text that appears beside the checkbox
20653 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20654 * @cfg {Boolean} checked initnal the element
20655 * @cfg {Boolean} inline inline the element (default false)
20656 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20657 * @cfg {String} tooltip label tooltip
20660 * Create a new CheckBox
20661 * @param {Object} config The config object
20664 Roo.bootstrap.CheckBox = function(config){
20665 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20670 * Fires when the element is checked or unchecked.
20671 * @param {Roo.bootstrap.CheckBox} this This input
20672 * @param {Boolean} checked The new checked value
20677 * Fires when the element is click.
20678 * @param {Roo.bootstrap.CheckBox} this This input
20685 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20687 inputType: 'checkbox',
20696 getAutoCreate : function()
20698 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20704 cfg.cls = 'form-group ' + this.inputType; //input-group
20707 cfg.cls += ' ' + this.inputType + '-inline';
20713 type : this.inputType,
20714 value : this.inputValue,
20715 cls : 'roo-' + this.inputType, //'form-box',
20716 placeholder : this.placeholder || ''
20720 if(this.inputType != 'radio'){
20724 cls : 'roo-hidden-value',
20725 value : this.checked ? this.inputValue : this.valueOff
20730 if (this.weight) { // Validity check?
20731 cfg.cls += " " + this.inputType + "-" + this.weight;
20734 if (this.disabled) {
20735 input.disabled=true;
20739 input.checked = this.checked;
20744 input.name = this.name;
20746 if(this.inputType != 'radio'){
20747 hidden.name = this.name;
20748 input.name = '_hidden_' + this.name;
20753 input.cls += ' input-' + this.size;
20758 ['xs','sm','md','lg'].map(function(size){
20759 if (settings[size]) {
20760 cfg.cls += ' col-' + size + '-' + settings[size];
20764 var inputblock = input;
20766 if (this.before || this.after) {
20769 cls : 'input-group',
20774 inputblock.cn.push({
20776 cls : 'input-group-addon',
20781 inputblock.cn.push(input);
20783 if(this.inputType != 'radio'){
20784 inputblock.cn.push(hidden);
20788 inputblock.cn.push({
20790 cls : 'input-group-addon',
20797 if (align ==='left' && this.fieldLabel.length) {
20798 // Roo.log("left and has label");
20803 cls : 'control-label',
20804 html : this.fieldLabel
20814 if(this.labelWidth > 12){
20815 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20818 if(this.labelWidth < 13 && this.labelmd == 0){
20819 this.labelmd = this.labelWidth;
20822 if(this.labellg > 0){
20823 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20824 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20827 if(this.labelmd > 0){
20828 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20829 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20832 if(this.labelsm > 0){
20833 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20834 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20837 if(this.labelxs > 0){
20838 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20839 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20842 } else if ( this.fieldLabel.length) {
20843 // Roo.log(" label");
20847 tag: this.boxLabel ? 'span' : 'label',
20849 cls: 'control-label box-input-label',
20850 //cls : 'input-group-addon',
20851 html : this.fieldLabel
20860 // Roo.log(" no label && no align");
20861 cfg.cn = [ inputblock ] ;
20867 var boxLabelCfg = {
20869 //'for': id, // box label is handled by onclick - so no for...
20871 html: this.boxLabel
20875 boxLabelCfg.tooltip = this.tooltip;
20878 cfg.cn.push(boxLabelCfg);
20881 if(this.inputType != 'radio'){
20882 cfg.cn.push(hidden);
20890 * return the real input element.
20892 inputEl: function ()
20894 return this.el.select('input.roo-' + this.inputType,true).first();
20896 hiddenEl: function ()
20898 return this.el.select('input.roo-hidden-value',true).first();
20901 labelEl: function()
20903 return this.el.select('label.control-label',true).first();
20905 /* depricated... */
20909 return this.labelEl();
20912 boxLabelEl: function()
20914 return this.el.select('label.box-label',true).first();
20917 initEvents : function()
20919 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20921 this.inputEl().on('click', this.onClick, this);
20923 if (this.boxLabel) {
20924 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20927 this.startValue = this.getValue();
20930 Roo.bootstrap.CheckBox.register(this);
20934 onClick : function(e)
20936 if(this.fireEvent('click', this, e) !== false){
20937 this.setChecked(!this.checked);
20942 setChecked : function(state,suppressEvent)
20944 this.startValue = this.getValue();
20946 if(this.inputType == 'radio'){
20948 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20949 e.dom.checked = false;
20952 this.inputEl().dom.checked = true;
20954 this.inputEl().dom.value = this.inputValue;
20956 if(suppressEvent !== true){
20957 this.fireEvent('check', this, true);
20965 this.checked = state;
20967 this.inputEl().dom.checked = state;
20970 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20972 if(suppressEvent !== true){
20973 this.fireEvent('check', this, state);
20979 getValue : function()
20981 if(this.inputType == 'radio'){
20982 return this.getGroupValue();
20985 return this.hiddenEl().dom.value;
20989 getGroupValue : function()
20991 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20995 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20998 setValue : function(v,suppressEvent)
21000 if(this.inputType == 'radio'){
21001 this.setGroupValue(v, suppressEvent);
21005 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21010 setGroupValue : function(v, suppressEvent)
21012 this.startValue = this.getValue();
21014 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21015 e.dom.checked = false;
21017 if(e.dom.value == v){
21018 e.dom.checked = true;
21022 if(suppressEvent !== true){
21023 this.fireEvent('check', this, true);
21031 validate : function()
21033 if(this.getVisibilityEl().hasClass('hidden')){
21039 (this.inputType == 'radio' && this.validateRadio()) ||
21040 (this.inputType == 'checkbox' && this.validateCheckbox())
21046 this.markInvalid();
21050 validateRadio : function()
21052 if(this.getVisibilityEl().hasClass('hidden')){
21056 if(this.allowBlank){
21062 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21063 if(!e.dom.checked){
21075 validateCheckbox : function()
21078 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21079 //return (this.getValue() == this.inputValue) ? true : false;
21082 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21090 for(var i in group){
21091 if(group[i].el.isVisible(true)){
21099 for(var i in group){
21104 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21111 * Mark this field as valid
21113 markValid : function()
21117 this.fireEvent('valid', this);
21119 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21122 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21129 if(this.inputType == 'radio'){
21130 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21132 e.findParent('.form-group', false, true).addClass(_this.validClass);
21139 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21140 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21144 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21150 for(var i in group){
21151 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21152 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21157 * Mark this field as invalid
21158 * @param {String} msg The validation message
21160 markInvalid : function(msg)
21162 if(this.allowBlank){
21168 this.fireEvent('invalid', this, msg);
21170 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21173 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21177 label.markInvalid();
21180 if(this.inputType == 'radio'){
21181 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21182 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21183 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21190 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21191 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21195 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21201 for(var i in group){
21202 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21203 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21208 clearInvalid : function()
21210 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21212 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21214 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21216 if (label && label.iconEl) {
21217 label.iconEl.removeClass(label.validClass);
21218 label.iconEl.removeClass(label.invalidClass);
21222 disable : function()
21224 if(this.inputType != 'radio'){
21225 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21232 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21233 _this.getActionEl().addClass(this.disabledClass);
21234 e.dom.disabled = true;
21238 this.disabled = true;
21239 this.fireEvent("disable", this);
21243 enable : function()
21245 if(this.inputType != 'radio'){
21246 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21253 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21254 _this.getActionEl().removeClass(this.disabledClass);
21255 e.dom.disabled = false;
21259 this.disabled = false;
21260 this.fireEvent("enable", this);
21264 setBoxLabel : function(v)
21269 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21275 Roo.apply(Roo.bootstrap.CheckBox, {
21280 * register a CheckBox Group
21281 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21283 register : function(checkbox)
21285 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21286 this.groups[checkbox.groupId] = {};
21289 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21293 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21297 * fetch a CheckBox Group based on the group ID
21298 * @param {string} the group ID
21299 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21301 get: function(groupId) {
21302 if (typeof(this.groups[groupId]) == 'undefined') {
21306 return this.groups[groupId] ;
21319 * @class Roo.bootstrap.Radio
21320 * @extends Roo.bootstrap.Component
21321 * Bootstrap Radio class
21322 * @cfg {String} boxLabel - the label associated
21323 * @cfg {String} value - the value of radio
21326 * Create a new Radio
21327 * @param {Object} config The config object
21329 Roo.bootstrap.Radio = function(config){
21330 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21334 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21340 getAutoCreate : function()
21344 cls : 'form-group radio',
21349 html : this.boxLabel
21357 initEvents : function()
21359 this.parent().register(this);
21361 this.el.on('click', this.onClick, this);
21365 onClick : function(e)
21367 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21368 this.setChecked(true);
21372 setChecked : function(state, suppressEvent)
21374 this.parent().setValue(this.value, suppressEvent);
21378 setBoxLabel : function(v)
21383 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21398 * @class Roo.bootstrap.SecurePass
21399 * @extends Roo.bootstrap.Input
21400 * Bootstrap SecurePass class
21404 * Create a new SecurePass
21405 * @param {Object} config The config object
21408 Roo.bootstrap.SecurePass = function (config) {
21409 // these go here, so the translation tool can replace them..
21411 PwdEmpty: "Please type a password, and then retype it to confirm.",
21412 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21413 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21414 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21415 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21416 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21417 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21418 TooWeak: "Your password is Too Weak."
21420 this.meterLabel = "Password strength:";
21421 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21422 this.meterClass = [
21423 "roo-password-meter-tooweak",
21424 "roo-password-meter-weak",
21425 "roo-password-meter-medium",
21426 "roo-password-meter-strong",
21427 "roo-password-meter-grey"
21432 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21435 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21437 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21439 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21440 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21441 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21442 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21443 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21444 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21445 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21455 * @cfg {String/Object} Label for the strength meter (defaults to
21456 * 'Password strength:')
21461 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21462 * ['Weak', 'Medium', 'Strong'])
21465 pwdStrengths: false,
21478 initEvents: function ()
21480 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21482 if (this.el.is('input[type=password]') && Roo.isSafari) {
21483 this.el.on('keydown', this.SafariOnKeyDown, this);
21486 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21489 onRender: function (ct, position)
21491 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21492 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21493 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21495 this.trigger.createChild({
21500 cls: 'roo-password-meter-grey col-xs-12',
21503 //width: this.meterWidth + 'px'
21507 cls: 'roo-password-meter-text'
21513 if (this.hideTrigger) {
21514 this.trigger.setDisplayed(false);
21516 this.setSize(this.width || '', this.height || '');
21519 onDestroy: function ()
21521 if (this.trigger) {
21522 this.trigger.removeAllListeners();
21523 this.trigger.remove();
21526 this.wrap.remove();
21528 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21531 checkStrength: function ()
21533 var pwd = this.inputEl().getValue();
21534 if (pwd == this._lastPwd) {
21539 if (this.ClientSideStrongPassword(pwd)) {
21541 } else if (this.ClientSideMediumPassword(pwd)) {
21543 } else if (this.ClientSideWeakPassword(pwd)) {
21549 Roo.log('strength1: ' + strength);
21551 //var pm = this.trigger.child('div/div/div').dom;
21552 var pm = this.trigger.child('div/div');
21553 pm.removeClass(this.meterClass);
21554 pm.addClass(this.meterClass[strength]);
21557 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21559 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21561 this._lastPwd = pwd;
21565 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21567 this._lastPwd = '';
21569 var pm = this.trigger.child('div/div');
21570 pm.removeClass(this.meterClass);
21571 pm.addClass('roo-password-meter-grey');
21574 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21577 this.inputEl().dom.type='password';
21580 validateValue: function (value)
21583 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21586 if (value.length == 0) {
21587 if (this.allowBlank) {
21588 this.clearInvalid();
21592 this.markInvalid(this.errors.PwdEmpty);
21593 this.errorMsg = this.errors.PwdEmpty;
21601 if ('[\x21-\x7e]*'.match(value)) {
21602 this.markInvalid(this.errors.PwdBadChar);
21603 this.errorMsg = this.errors.PwdBadChar;
21606 if (value.length < 6) {
21607 this.markInvalid(this.errors.PwdShort);
21608 this.errorMsg = this.errors.PwdShort;
21611 if (value.length > 16) {
21612 this.markInvalid(this.errors.PwdLong);
21613 this.errorMsg = this.errors.PwdLong;
21617 if (this.ClientSideStrongPassword(value)) {
21619 } else if (this.ClientSideMediumPassword(value)) {
21621 } else if (this.ClientSideWeakPassword(value)) {
21628 if (strength < 2) {
21629 //this.markInvalid(this.errors.TooWeak);
21630 this.errorMsg = this.errors.TooWeak;
21635 console.log('strength2: ' + strength);
21637 //var pm = this.trigger.child('div/div/div').dom;
21639 var pm = this.trigger.child('div/div');
21640 pm.removeClass(this.meterClass);
21641 pm.addClass(this.meterClass[strength]);
21643 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21645 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21647 this.errorMsg = '';
21651 CharacterSetChecks: function (type)
21654 this.fResult = false;
21657 isctype: function (character, type)
21660 case this.kCapitalLetter:
21661 if (character >= 'A' && character <= 'Z') {
21666 case this.kSmallLetter:
21667 if (character >= 'a' && character <= 'z') {
21673 if (character >= '0' && character <= '9') {
21678 case this.kPunctuation:
21679 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21690 IsLongEnough: function (pwd, size)
21692 return !(pwd == null || isNaN(size) || pwd.length < size);
21695 SpansEnoughCharacterSets: function (word, nb)
21697 if (!this.IsLongEnough(word, nb))
21702 var characterSetChecks = new Array(
21703 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21704 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21707 for (var index = 0; index < word.length; ++index) {
21708 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21709 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21710 characterSetChecks[nCharSet].fResult = true;
21717 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21718 if (characterSetChecks[nCharSet].fResult) {
21723 if (nCharSets < nb) {
21729 ClientSideStrongPassword: function (pwd)
21731 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21734 ClientSideMediumPassword: function (pwd)
21736 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21739 ClientSideWeakPassword: function (pwd)
21741 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21744 })//<script type="text/javascript">
21747 * Based Ext JS Library 1.1.1
21748 * Copyright(c) 2006-2007, Ext JS, LLC.
21754 * @class Roo.HtmlEditorCore
21755 * @extends Roo.Component
21756 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21758 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21761 Roo.HtmlEditorCore = function(config){
21764 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21769 * @event initialize
21770 * Fires when the editor is fully initialized (including the iframe)
21771 * @param {Roo.HtmlEditorCore} this
21776 * Fires when the editor is first receives the focus. Any insertion must wait
21777 * until after this event.
21778 * @param {Roo.HtmlEditorCore} this
21782 * @event beforesync
21783 * Fires before the textarea is updated with content from the editor iframe. Return false
21784 * to cancel the sync.
21785 * @param {Roo.HtmlEditorCore} this
21786 * @param {String} html
21790 * @event beforepush
21791 * Fires before the iframe editor is updated with content from the textarea. Return false
21792 * to cancel the push.
21793 * @param {Roo.HtmlEditorCore} this
21794 * @param {String} html
21799 * Fires when the textarea is updated with content from the editor iframe.
21800 * @param {Roo.HtmlEditorCore} this
21801 * @param {String} html
21806 * Fires when the iframe editor is updated with content from the textarea.
21807 * @param {Roo.HtmlEditorCore} this
21808 * @param {String} html
21813 * @event editorevent
21814 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21815 * @param {Roo.HtmlEditorCore} this
21821 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21823 // defaults : white / black...
21824 this.applyBlacklists();
21831 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21835 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21841 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21846 * @cfg {Number} height (in pixels)
21850 * @cfg {Number} width (in pixels)
21855 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21858 stylesheets: false,
21863 // private properties
21864 validationEvent : false,
21866 initialized : false,
21868 sourceEditMode : false,
21869 onFocus : Roo.emptyFn,
21871 hideMode:'offsets',
21875 // blacklist + whitelisted elements..
21882 * Protected method that will not generally be called directly. It
21883 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21884 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21886 getDocMarkup : function(){
21890 // inherit styels from page...??
21891 if (this.stylesheets === false) {
21893 Roo.get(document.head).select('style').each(function(node) {
21894 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21897 Roo.get(document.head).select('link').each(function(node) {
21898 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21901 } else if (!this.stylesheets.length) {
21903 st = '<style type="text/css">' +
21904 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21907 st = '<style type="text/css">' +
21912 st += '<style type="text/css">' +
21913 'IMG { cursor: pointer } ' +
21916 var cls = 'roo-htmleditor-body';
21918 if(this.bodyCls.length){
21919 cls += ' ' + this.bodyCls;
21922 return '<html><head>' + st +
21923 //<style type="text/css">' +
21924 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21926 ' </head><body class="' + cls + '"></body></html>';
21930 onRender : function(ct, position)
21933 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21934 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21937 this.el.dom.style.border = '0 none';
21938 this.el.dom.setAttribute('tabIndex', -1);
21939 this.el.addClass('x-hidden hide');
21943 if(Roo.isIE){ // fix IE 1px bogus margin
21944 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21948 this.frameId = Roo.id();
21952 var iframe = this.owner.wrap.createChild({
21954 cls: 'form-control', // bootstrap..
21956 name: this.frameId,
21957 frameBorder : 'no',
21958 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21963 this.iframe = iframe.dom;
21965 this.assignDocWin();
21967 this.doc.designMode = 'on';
21970 this.doc.write(this.getDocMarkup());
21974 var task = { // must defer to wait for browser to be ready
21976 //console.log("run task?" + this.doc.readyState);
21977 this.assignDocWin();
21978 if(this.doc.body || this.doc.readyState == 'complete'){
21980 this.doc.designMode="on";
21984 Roo.TaskMgr.stop(task);
21985 this.initEditor.defer(10, this);
21992 Roo.TaskMgr.start(task);
21997 onResize : function(w, h)
21999 Roo.log('resize: ' +w + ',' + h );
22000 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22004 if(typeof w == 'number'){
22006 this.iframe.style.width = w + 'px';
22008 if(typeof h == 'number'){
22010 this.iframe.style.height = h + 'px';
22012 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22019 * Toggles the editor between standard and source edit mode.
22020 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22022 toggleSourceEdit : function(sourceEditMode){
22024 this.sourceEditMode = sourceEditMode === true;
22026 if(this.sourceEditMode){
22028 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22031 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22032 //this.iframe.className = '';
22035 //this.setSize(this.owner.wrap.getSize());
22036 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22043 * Protected method that will not generally be called directly. If you need/want
22044 * custom HTML cleanup, this is the method you should override.
22045 * @param {String} html The HTML to be cleaned
22046 * return {String} The cleaned HTML
22048 cleanHtml : function(html){
22049 html = String(html);
22050 if(html.length > 5){
22051 if(Roo.isSafari){ // strip safari nonsense
22052 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22055 if(html == ' '){
22062 * HTML Editor -> Textarea
22063 * Protected method that will not generally be called directly. Syncs the contents
22064 * of the editor iframe with the textarea.
22066 syncValue : function(){
22067 if(this.initialized){
22068 var bd = (this.doc.body || this.doc.documentElement);
22069 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22070 var html = bd.innerHTML;
22072 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22073 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22075 html = '<div style="'+m[0]+'">' + html + '</div>';
22078 html = this.cleanHtml(html);
22079 // fix up the special chars.. normaly like back quotes in word...
22080 // however we do not want to do this with chinese..
22081 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22082 var cc = b.charCodeAt();
22084 (cc >= 0x4E00 && cc < 0xA000 ) ||
22085 (cc >= 0x3400 && cc < 0x4E00 ) ||
22086 (cc >= 0xf900 && cc < 0xfb00 )
22092 if(this.owner.fireEvent('beforesync', this, html) !== false){
22093 this.el.dom.value = html;
22094 this.owner.fireEvent('sync', this, html);
22100 * Protected method that will not generally be called directly. Pushes the value of the textarea
22101 * into the iframe editor.
22103 pushValue : function(){
22104 if(this.initialized){
22105 var v = this.el.dom.value.trim();
22107 // if(v.length < 1){
22111 if(this.owner.fireEvent('beforepush', this, v) !== false){
22112 var d = (this.doc.body || this.doc.documentElement);
22114 this.cleanUpPaste();
22115 this.el.dom.value = d.innerHTML;
22116 this.owner.fireEvent('push', this, v);
22122 deferFocus : function(){
22123 this.focus.defer(10, this);
22127 focus : function(){
22128 if(this.win && !this.sourceEditMode){
22135 assignDocWin: function()
22137 var iframe = this.iframe;
22140 this.doc = iframe.contentWindow.document;
22141 this.win = iframe.contentWindow;
22143 // if (!Roo.get(this.frameId)) {
22146 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22147 // this.win = Roo.get(this.frameId).dom.contentWindow;
22149 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22153 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22154 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22159 initEditor : function(){
22160 //console.log("INIT EDITOR");
22161 this.assignDocWin();
22165 this.doc.designMode="on";
22167 this.doc.write(this.getDocMarkup());
22170 var dbody = (this.doc.body || this.doc.documentElement);
22171 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22172 // this copies styles from the containing element into thsi one..
22173 // not sure why we need all of this..
22174 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22176 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22177 //ss['background-attachment'] = 'fixed'; // w3c
22178 dbody.bgProperties = 'fixed'; // ie
22179 //Roo.DomHelper.applyStyles(dbody, ss);
22180 Roo.EventManager.on(this.doc, {
22181 //'mousedown': this.onEditorEvent,
22182 'mouseup': this.onEditorEvent,
22183 'dblclick': this.onEditorEvent,
22184 'click': this.onEditorEvent,
22185 'keyup': this.onEditorEvent,
22190 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22192 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22193 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22195 this.initialized = true;
22197 this.owner.fireEvent('initialize', this);
22202 onDestroy : function(){
22208 //for (var i =0; i < this.toolbars.length;i++) {
22209 // // fixme - ask toolbars for heights?
22210 // this.toolbars[i].onDestroy();
22213 //this.wrap.dom.innerHTML = '';
22214 //this.wrap.remove();
22219 onFirstFocus : function(){
22221 this.assignDocWin();
22224 this.activated = true;
22227 if(Roo.isGecko){ // prevent silly gecko errors
22229 var s = this.win.getSelection();
22230 if(!s.focusNode || s.focusNode.nodeType != 3){
22231 var r = s.getRangeAt(0);
22232 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22237 this.execCmd('useCSS', true);
22238 this.execCmd('styleWithCSS', false);
22241 this.owner.fireEvent('activate', this);
22245 adjustFont: function(btn){
22246 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22247 //if(Roo.isSafari){ // safari
22250 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22251 if(Roo.isSafari){ // safari
22252 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22253 v = (v < 10) ? 10 : v;
22254 v = (v > 48) ? 48 : v;
22255 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22260 v = Math.max(1, v+adjust);
22262 this.execCmd('FontSize', v );
22265 onEditorEvent : function(e)
22267 this.owner.fireEvent('editorevent', this, e);
22268 // this.updateToolbar();
22269 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22272 insertTag : function(tg)
22274 // could be a bit smarter... -> wrap the current selected tRoo..
22275 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22277 range = this.createRange(this.getSelection());
22278 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22279 wrappingNode.appendChild(range.extractContents());
22280 range.insertNode(wrappingNode);
22287 this.execCmd("formatblock", tg);
22291 insertText : function(txt)
22295 var range = this.createRange();
22296 range.deleteContents();
22297 //alert(Sender.getAttribute('label'));
22299 range.insertNode(this.doc.createTextNode(txt));
22305 * Executes a Midas editor command on the editor document and performs necessary focus and
22306 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22307 * @param {String} cmd The Midas command
22308 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22310 relayCmd : function(cmd, value){
22312 this.execCmd(cmd, value);
22313 this.owner.fireEvent('editorevent', this);
22314 //this.updateToolbar();
22315 this.owner.deferFocus();
22319 * Executes a Midas editor command directly on the editor document.
22320 * For visual commands, you should use {@link #relayCmd} instead.
22321 * <b>This should only be called after the editor is initialized.</b>
22322 * @param {String} cmd The Midas command
22323 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22325 execCmd : function(cmd, value){
22326 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22333 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22335 * @param {String} text | dom node..
22337 insertAtCursor : function(text)
22340 if(!this.activated){
22346 var r = this.doc.selection.createRange();
22357 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22361 // from jquery ui (MIT licenced)
22363 var win = this.win;
22365 if (win.getSelection && win.getSelection().getRangeAt) {
22366 range = win.getSelection().getRangeAt(0);
22367 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22368 range.insertNode(node);
22369 } else if (win.document.selection && win.document.selection.createRange) {
22370 // no firefox support
22371 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22372 win.document.selection.createRange().pasteHTML(txt);
22374 // no firefox support
22375 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22376 this.execCmd('InsertHTML', txt);
22385 mozKeyPress : function(e){
22387 var c = e.getCharCode(), cmd;
22390 c = String.fromCharCode(c).toLowerCase();
22404 this.cleanUpPaste.defer(100, this);
22412 e.preventDefault();
22420 fixKeys : function(){ // load time branching for fastest keydown performance
22422 return function(e){
22423 var k = e.getKey(), r;
22426 r = this.doc.selection.createRange();
22429 r.pasteHTML('    ');
22436 r = this.doc.selection.createRange();
22438 var target = r.parentElement();
22439 if(!target || target.tagName.toLowerCase() != 'li'){
22441 r.pasteHTML('<br />');
22447 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22448 this.cleanUpPaste.defer(100, this);
22454 }else if(Roo.isOpera){
22455 return function(e){
22456 var k = e.getKey();
22460 this.execCmd('InsertHTML','    ');
22463 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22464 this.cleanUpPaste.defer(100, this);
22469 }else if(Roo.isSafari){
22470 return function(e){
22471 var k = e.getKey();
22475 this.execCmd('InsertText','\t');
22479 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22480 this.cleanUpPaste.defer(100, this);
22488 getAllAncestors: function()
22490 var p = this.getSelectedNode();
22493 a.push(p); // push blank onto stack..
22494 p = this.getParentElement();
22498 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22502 a.push(this.doc.body);
22506 lastSelNode : false,
22509 getSelection : function()
22511 this.assignDocWin();
22512 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22515 getSelectedNode: function()
22517 // this may only work on Gecko!!!
22519 // should we cache this!!!!
22524 var range = this.createRange(this.getSelection()).cloneRange();
22527 var parent = range.parentElement();
22529 var testRange = range.duplicate();
22530 testRange.moveToElementText(parent);
22531 if (testRange.inRange(range)) {
22534 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22537 parent = parent.parentElement;
22542 // is ancestor a text element.
22543 var ac = range.commonAncestorContainer;
22544 if (ac.nodeType == 3) {
22545 ac = ac.parentNode;
22548 var ar = ac.childNodes;
22551 var other_nodes = [];
22552 var has_other_nodes = false;
22553 for (var i=0;i<ar.length;i++) {
22554 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22557 // fullly contained node.
22559 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22564 // probably selected..
22565 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22566 other_nodes.push(ar[i]);
22570 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22575 has_other_nodes = true;
22577 if (!nodes.length && other_nodes.length) {
22578 nodes= other_nodes;
22580 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22586 createRange: function(sel)
22588 // this has strange effects when using with
22589 // top toolbar - not sure if it's a great idea.
22590 //this.editor.contentWindow.focus();
22591 if (typeof sel != "undefined") {
22593 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22595 return this.doc.createRange();
22598 return this.doc.createRange();
22601 getParentElement: function()
22604 this.assignDocWin();
22605 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22607 var range = this.createRange(sel);
22610 var p = range.commonAncestorContainer;
22611 while (p.nodeType == 3) { // text node
22622 * Range intersection.. the hard stuff...
22626 * [ -- selected range --- ]
22630 * if end is before start or hits it. fail.
22631 * if start is after end or hits it fail.
22633 * if either hits (but other is outside. - then it's not
22639 // @see http://www.thismuchiknow.co.uk/?p=64.
22640 rangeIntersectsNode : function(range, node)
22642 var nodeRange = node.ownerDocument.createRange();
22644 nodeRange.selectNode(node);
22646 nodeRange.selectNodeContents(node);
22649 var rangeStartRange = range.cloneRange();
22650 rangeStartRange.collapse(true);
22652 var rangeEndRange = range.cloneRange();
22653 rangeEndRange.collapse(false);
22655 var nodeStartRange = nodeRange.cloneRange();
22656 nodeStartRange.collapse(true);
22658 var nodeEndRange = nodeRange.cloneRange();
22659 nodeEndRange.collapse(false);
22661 return rangeStartRange.compareBoundaryPoints(
22662 Range.START_TO_START, nodeEndRange) == -1 &&
22663 rangeEndRange.compareBoundaryPoints(
22664 Range.START_TO_START, nodeStartRange) == 1;
22668 rangeCompareNode : function(range, node)
22670 var nodeRange = node.ownerDocument.createRange();
22672 nodeRange.selectNode(node);
22674 nodeRange.selectNodeContents(node);
22678 range.collapse(true);
22680 nodeRange.collapse(true);
22682 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22683 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22685 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22687 var nodeIsBefore = ss == 1;
22688 var nodeIsAfter = ee == -1;
22690 if (nodeIsBefore && nodeIsAfter) {
22693 if (!nodeIsBefore && nodeIsAfter) {
22694 return 1; //right trailed.
22697 if (nodeIsBefore && !nodeIsAfter) {
22698 return 2; // left trailed.
22704 // private? - in a new class?
22705 cleanUpPaste : function()
22707 // cleans up the whole document..
22708 Roo.log('cleanuppaste');
22710 this.cleanUpChildren(this.doc.body);
22711 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22712 if (clean != this.doc.body.innerHTML) {
22713 this.doc.body.innerHTML = clean;
22718 cleanWordChars : function(input) {// change the chars to hex code
22719 var he = Roo.HtmlEditorCore;
22721 var output = input;
22722 Roo.each(he.swapCodes, function(sw) {
22723 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22725 output = output.replace(swapper, sw[1]);
22732 cleanUpChildren : function (n)
22734 if (!n.childNodes.length) {
22737 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22738 this.cleanUpChild(n.childNodes[i]);
22745 cleanUpChild : function (node)
22748 //console.log(node);
22749 if (node.nodeName == "#text") {
22750 // clean up silly Windows -- stuff?
22753 if (node.nodeName == "#comment") {
22754 node.parentNode.removeChild(node);
22755 // clean up silly Windows -- stuff?
22758 var lcname = node.tagName.toLowerCase();
22759 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22760 // whitelist of tags..
22762 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22764 node.parentNode.removeChild(node);
22769 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22771 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22772 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22774 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22775 // remove_keep_children = true;
22778 if (remove_keep_children) {
22779 this.cleanUpChildren(node);
22780 // inserts everything just before this node...
22781 while (node.childNodes.length) {
22782 var cn = node.childNodes[0];
22783 node.removeChild(cn);
22784 node.parentNode.insertBefore(cn, node);
22786 node.parentNode.removeChild(node);
22790 if (!node.attributes || !node.attributes.length) {
22791 this.cleanUpChildren(node);
22795 function cleanAttr(n,v)
22798 if (v.match(/^\./) || v.match(/^\//)) {
22801 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22804 if (v.match(/^#/)) {
22807 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22808 node.removeAttribute(n);
22812 var cwhite = this.cwhite;
22813 var cblack = this.cblack;
22815 function cleanStyle(n,v)
22817 if (v.match(/expression/)) { //XSS?? should we even bother..
22818 node.removeAttribute(n);
22822 var parts = v.split(/;/);
22825 Roo.each(parts, function(p) {
22826 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22830 var l = p.split(':').shift().replace(/\s+/g,'');
22831 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22833 if ( cwhite.length && cblack.indexOf(l) > -1) {
22834 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22835 //node.removeAttribute(n);
22839 // only allow 'c whitelisted system attributes'
22840 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22841 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22842 //node.removeAttribute(n);
22852 if (clean.length) {
22853 node.setAttribute(n, clean.join(';'));
22855 node.removeAttribute(n);
22861 for (var i = node.attributes.length-1; i > -1 ; i--) {
22862 var a = node.attributes[i];
22865 if (a.name.toLowerCase().substr(0,2)=='on') {
22866 node.removeAttribute(a.name);
22869 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22870 node.removeAttribute(a.name);
22873 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22874 cleanAttr(a.name,a.value); // fixme..
22877 if (a.name == 'style') {
22878 cleanStyle(a.name,a.value);
22881 /// clean up MS crap..
22882 // tecnically this should be a list of valid class'es..
22885 if (a.name == 'class') {
22886 if (a.value.match(/^Mso/)) {
22887 node.className = '';
22890 if (a.value.match(/^body$/)) {
22891 node.className = '';
22902 this.cleanUpChildren(node);
22908 * Clean up MS wordisms...
22910 cleanWord : function(node)
22915 this.cleanWord(this.doc.body);
22918 if (node.nodeName == "#text") {
22919 // clean up silly Windows -- stuff?
22922 if (node.nodeName == "#comment") {
22923 node.parentNode.removeChild(node);
22924 // clean up silly Windows -- stuff?
22928 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22929 node.parentNode.removeChild(node);
22933 // remove - but keep children..
22934 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22935 while (node.childNodes.length) {
22936 var cn = node.childNodes[0];
22937 node.removeChild(cn);
22938 node.parentNode.insertBefore(cn, node);
22940 node.parentNode.removeChild(node);
22941 this.iterateChildren(node, this.cleanWord);
22945 if (node.className.length) {
22947 var cn = node.className.split(/\W+/);
22949 Roo.each(cn, function(cls) {
22950 if (cls.match(/Mso[a-zA-Z]+/)) {
22955 node.className = cna.length ? cna.join(' ') : '';
22957 node.removeAttribute("class");
22961 if (node.hasAttribute("lang")) {
22962 node.removeAttribute("lang");
22965 if (node.hasAttribute("style")) {
22967 var styles = node.getAttribute("style").split(";");
22969 Roo.each(styles, function(s) {
22970 if (!s.match(/:/)) {
22973 var kv = s.split(":");
22974 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22977 // what ever is left... we allow.
22980 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22981 if (!nstyle.length) {
22982 node.removeAttribute('style');
22985 this.iterateChildren(node, this.cleanWord);
22991 * iterateChildren of a Node, calling fn each time, using this as the scole..
22992 * @param {DomNode} node node to iterate children of.
22993 * @param {Function} fn method of this class to call on each item.
22995 iterateChildren : function(node, fn)
22997 if (!node.childNodes.length) {
23000 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23001 fn.call(this, node.childNodes[i])
23007 * cleanTableWidths.
23009 * Quite often pasting from word etc.. results in tables with column and widths.
23010 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23013 cleanTableWidths : function(node)
23018 this.cleanTableWidths(this.doc.body);
23023 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23026 Roo.log(node.tagName);
23027 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23028 this.iterateChildren(node, this.cleanTableWidths);
23031 if (node.hasAttribute('width')) {
23032 node.removeAttribute('width');
23036 if (node.hasAttribute("style")) {
23039 var styles = node.getAttribute("style").split(";");
23041 Roo.each(styles, function(s) {
23042 if (!s.match(/:/)) {
23045 var kv = s.split(":");
23046 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23049 // what ever is left... we allow.
23052 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23053 if (!nstyle.length) {
23054 node.removeAttribute('style');
23058 this.iterateChildren(node, this.cleanTableWidths);
23066 domToHTML : function(currentElement, depth, nopadtext) {
23068 depth = depth || 0;
23069 nopadtext = nopadtext || false;
23071 if (!currentElement) {
23072 return this.domToHTML(this.doc.body);
23075 //Roo.log(currentElement);
23077 var allText = false;
23078 var nodeName = currentElement.nodeName;
23079 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23081 if (nodeName == '#text') {
23083 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23088 if (nodeName != 'BODY') {
23091 // Prints the node tagName, such as <A>, <IMG>, etc
23094 for(i = 0; i < currentElement.attributes.length;i++) {
23096 var aname = currentElement.attributes.item(i).name;
23097 if (!currentElement.attributes.item(i).value.length) {
23100 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23103 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23112 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23115 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23120 // Traverse the tree
23122 var currentElementChild = currentElement.childNodes.item(i);
23123 var allText = true;
23124 var innerHTML = '';
23126 while (currentElementChild) {
23127 // Formatting code (indent the tree so it looks nice on the screen)
23128 var nopad = nopadtext;
23129 if (lastnode == 'SPAN') {
23133 if (currentElementChild.nodeName == '#text') {
23134 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23135 toadd = nopadtext ? toadd : toadd.trim();
23136 if (!nopad && toadd.length > 80) {
23137 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23139 innerHTML += toadd;
23142 currentElementChild = currentElement.childNodes.item(i);
23148 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23150 // Recursively traverse the tree structure of the child node
23151 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23152 lastnode = currentElementChild.nodeName;
23154 currentElementChild=currentElement.childNodes.item(i);
23160 // The remaining code is mostly for formatting the tree
23161 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23166 ret+= "</"+tagName+">";
23172 applyBlacklists : function()
23174 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23175 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23179 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23180 if (b.indexOf(tag) > -1) {
23183 this.white.push(tag);
23187 Roo.each(w, function(tag) {
23188 if (b.indexOf(tag) > -1) {
23191 if (this.white.indexOf(tag) > -1) {
23194 this.white.push(tag);
23199 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23200 if (w.indexOf(tag) > -1) {
23203 this.black.push(tag);
23207 Roo.each(b, function(tag) {
23208 if (w.indexOf(tag) > -1) {
23211 if (this.black.indexOf(tag) > -1) {
23214 this.black.push(tag);
23219 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23220 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23224 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23225 if (b.indexOf(tag) > -1) {
23228 this.cwhite.push(tag);
23232 Roo.each(w, function(tag) {
23233 if (b.indexOf(tag) > -1) {
23236 if (this.cwhite.indexOf(tag) > -1) {
23239 this.cwhite.push(tag);
23244 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23245 if (w.indexOf(tag) > -1) {
23248 this.cblack.push(tag);
23252 Roo.each(b, function(tag) {
23253 if (w.indexOf(tag) > -1) {
23256 if (this.cblack.indexOf(tag) > -1) {
23259 this.cblack.push(tag);
23264 setStylesheets : function(stylesheets)
23266 if(typeof(stylesheets) == 'string'){
23267 Roo.get(this.iframe.contentDocument.head).createChild({
23269 rel : 'stylesheet',
23278 Roo.each(stylesheets, function(s) {
23283 Roo.get(_this.iframe.contentDocument.head).createChild({
23285 rel : 'stylesheet',
23294 removeStylesheets : function()
23298 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23303 setStyle : function(style)
23305 Roo.get(this.iframe.contentDocument.head).createChild({
23314 // hide stuff that is not compatible
23328 * @event specialkey
23332 * @cfg {String} fieldClass @hide
23335 * @cfg {String} focusClass @hide
23338 * @cfg {String} autoCreate @hide
23341 * @cfg {String} inputType @hide
23344 * @cfg {String} invalidClass @hide
23347 * @cfg {String} invalidText @hide
23350 * @cfg {String} msgFx @hide
23353 * @cfg {String} validateOnBlur @hide
23357 Roo.HtmlEditorCore.white = [
23358 'area', 'br', 'img', 'input', 'hr', 'wbr',
23360 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23361 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23362 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23363 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23364 'table', 'ul', 'xmp',
23366 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23369 'dir', 'menu', 'ol', 'ul', 'dl',
23375 Roo.HtmlEditorCore.black = [
23376 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23378 'base', 'basefont', 'bgsound', 'blink', 'body',
23379 'frame', 'frameset', 'head', 'html', 'ilayer',
23380 'iframe', 'layer', 'link', 'meta', 'object',
23381 'script', 'style' ,'title', 'xml' // clean later..
23383 Roo.HtmlEditorCore.clean = [
23384 'script', 'style', 'title', 'xml'
23386 Roo.HtmlEditorCore.remove = [
23391 Roo.HtmlEditorCore.ablack = [
23395 Roo.HtmlEditorCore.aclean = [
23396 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23400 Roo.HtmlEditorCore.pwhite= [
23401 'http', 'https', 'mailto'
23404 // white listed style attributes.
23405 Roo.HtmlEditorCore.cwhite= [
23406 // 'text-align', /// default is to allow most things..
23412 // black listed style attributes.
23413 Roo.HtmlEditorCore.cblack= [
23414 // 'font-size' -- this can be set by the project
23418 Roo.HtmlEditorCore.swapCodes =[
23437 * @class Roo.bootstrap.HtmlEditor
23438 * @extends Roo.bootstrap.TextArea
23439 * Bootstrap HtmlEditor class
23442 * Create a new HtmlEditor
23443 * @param {Object} config The config object
23446 Roo.bootstrap.HtmlEditor = function(config){
23447 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23448 if (!this.toolbars) {
23449 this.toolbars = [];
23452 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23455 * @event initialize
23456 * Fires when the editor is fully initialized (including the iframe)
23457 * @param {HtmlEditor} this
23462 * Fires when the editor is first receives the focus. Any insertion must wait
23463 * until after this event.
23464 * @param {HtmlEditor} this
23468 * @event beforesync
23469 * Fires before the textarea is updated with content from the editor iframe. Return false
23470 * to cancel the sync.
23471 * @param {HtmlEditor} this
23472 * @param {String} html
23476 * @event beforepush
23477 * Fires before the iframe editor is updated with content from the textarea. Return false
23478 * to cancel the push.
23479 * @param {HtmlEditor} this
23480 * @param {String} html
23485 * Fires when the textarea is updated with content from the editor iframe.
23486 * @param {HtmlEditor} this
23487 * @param {String} html
23492 * Fires when the iframe editor is updated with content from the textarea.
23493 * @param {HtmlEditor} this
23494 * @param {String} html
23498 * @event editmodechange
23499 * Fires when the editor switches edit modes
23500 * @param {HtmlEditor} this
23501 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23503 editmodechange: true,
23505 * @event editorevent
23506 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23507 * @param {HtmlEditor} this
23511 * @event firstfocus
23512 * Fires when on first focus - needed by toolbars..
23513 * @param {HtmlEditor} this
23518 * Auto save the htmlEditor value as a file into Events
23519 * @param {HtmlEditor} this
23523 * @event savedpreview
23524 * preview the saved version of htmlEditor
23525 * @param {HtmlEditor} this
23532 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23536 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23541 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23546 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23551 * @cfg {Number} height (in pixels)
23555 * @cfg {Number} width (in pixels)
23560 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23563 stylesheets: false,
23568 // private properties
23569 validationEvent : false,
23571 initialized : false,
23574 onFocus : Roo.emptyFn,
23576 hideMode:'offsets',
23578 tbContainer : false,
23582 toolbarContainer :function() {
23583 return this.wrap.select('.x-html-editor-tb',true).first();
23587 * Protected method that will not generally be called directly. It
23588 * is called when the editor creates its toolbar. Override this method if you need to
23589 * add custom toolbar buttons.
23590 * @param {HtmlEditor} editor
23592 createToolbar : function(){
23593 Roo.log('renewing');
23594 Roo.log("create toolbars");
23596 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23597 this.toolbars[0].render(this.toolbarContainer());
23601 // if (!editor.toolbars || !editor.toolbars.length) {
23602 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23605 // for (var i =0 ; i < editor.toolbars.length;i++) {
23606 // editor.toolbars[i] = Roo.factory(
23607 // typeof(editor.toolbars[i]) == 'string' ?
23608 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23609 // Roo.bootstrap.HtmlEditor);
23610 // editor.toolbars[i].init(editor);
23616 onRender : function(ct, position)
23618 // Roo.log("Call onRender: " + this.xtype);
23620 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23622 this.wrap = this.inputEl().wrap({
23623 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23626 this.editorcore.onRender(ct, position);
23628 if (this.resizable) {
23629 this.resizeEl = new Roo.Resizable(this.wrap, {
23633 minHeight : this.height,
23634 height: this.height,
23635 handles : this.resizable,
23638 resize : function(r, w, h) {
23639 _t.onResize(w,h); // -something
23645 this.createToolbar(this);
23648 if(!this.width && this.resizable){
23649 this.setSize(this.wrap.getSize());
23651 if (this.resizeEl) {
23652 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23653 // should trigger onReize..
23659 onResize : function(w, h)
23661 Roo.log('resize: ' +w + ',' + h );
23662 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23666 if(this.inputEl() ){
23667 if(typeof w == 'number'){
23668 var aw = w - this.wrap.getFrameWidth('lr');
23669 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23672 if(typeof h == 'number'){
23673 var tbh = -11; // fixme it needs to tool bar size!
23674 for (var i =0; i < this.toolbars.length;i++) {
23675 // fixme - ask toolbars for heights?
23676 tbh += this.toolbars[i].el.getHeight();
23677 //if (this.toolbars[i].footer) {
23678 // tbh += this.toolbars[i].footer.el.getHeight();
23686 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23687 ah -= 5; // knock a few pixes off for look..
23688 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23692 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23693 this.editorcore.onResize(ew,eh);
23698 * Toggles the editor between standard and source edit mode.
23699 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23701 toggleSourceEdit : function(sourceEditMode)
23703 this.editorcore.toggleSourceEdit(sourceEditMode);
23705 if(this.editorcore.sourceEditMode){
23706 Roo.log('editor - showing textarea');
23709 // Roo.log(this.syncValue());
23711 this.inputEl().removeClass(['hide', 'x-hidden']);
23712 this.inputEl().dom.removeAttribute('tabIndex');
23713 this.inputEl().focus();
23715 Roo.log('editor - hiding textarea');
23717 // Roo.log(this.pushValue());
23720 this.inputEl().addClass(['hide', 'x-hidden']);
23721 this.inputEl().dom.setAttribute('tabIndex', -1);
23722 //this.deferFocus();
23725 if(this.resizable){
23726 this.setSize(this.wrap.getSize());
23729 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23732 // private (for BoxComponent)
23733 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23735 // private (for BoxComponent)
23736 getResizeEl : function(){
23740 // private (for BoxComponent)
23741 getPositionEl : function(){
23746 initEvents : function(){
23747 this.originalValue = this.getValue();
23751 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23754 // markInvalid : Roo.emptyFn,
23756 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23759 // clearInvalid : Roo.emptyFn,
23761 setValue : function(v){
23762 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23763 this.editorcore.pushValue();
23768 deferFocus : function(){
23769 this.focus.defer(10, this);
23773 focus : function(){
23774 this.editorcore.focus();
23780 onDestroy : function(){
23786 for (var i =0; i < this.toolbars.length;i++) {
23787 // fixme - ask toolbars for heights?
23788 this.toolbars[i].onDestroy();
23791 this.wrap.dom.innerHTML = '';
23792 this.wrap.remove();
23797 onFirstFocus : function(){
23798 //Roo.log("onFirstFocus");
23799 this.editorcore.onFirstFocus();
23800 for (var i =0; i < this.toolbars.length;i++) {
23801 this.toolbars[i].onFirstFocus();
23807 syncValue : function()
23809 this.editorcore.syncValue();
23812 pushValue : function()
23814 this.editorcore.pushValue();
23818 // hide stuff that is not compatible
23832 * @event specialkey
23836 * @cfg {String} fieldClass @hide
23839 * @cfg {String} focusClass @hide
23842 * @cfg {String} autoCreate @hide
23845 * @cfg {String} inputType @hide
23848 * @cfg {String} invalidClass @hide
23851 * @cfg {String} invalidText @hide
23854 * @cfg {String} msgFx @hide
23857 * @cfg {String} validateOnBlur @hide
23866 Roo.namespace('Roo.bootstrap.htmleditor');
23868 * @class Roo.bootstrap.HtmlEditorToolbar1
23873 new Roo.bootstrap.HtmlEditor({
23876 new Roo.bootstrap.HtmlEditorToolbar1({
23877 disable : { fonts: 1 , format: 1, ..., ... , ...],
23883 * @cfg {Object} disable List of elements to disable..
23884 * @cfg {Array} btns List of additional buttons.
23888 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23891 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23894 Roo.apply(this, config);
23896 // default disabled, based on 'good practice'..
23897 this.disable = this.disable || {};
23898 Roo.applyIf(this.disable, {
23901 specialElements : true
23903 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23905 this.editor = config.editor;
23906 this.editorcore = config.editor.editorcore;
23908 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23910 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23911 // dont call parent... till later.
23913 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23918 editorcore : false,
23923 "h1","h2","h3","h4","h5","h6",
23925 "abbr", "acronym", "address", "cite", "samp", "var",
23929 onRender : function(ct, position)
23931 // Roo.log("Call onRender: " + this.xtype);
23933 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23935 this.el.dom.style.marginBottom = '0';
23937 var editorcore = this.editorcore;
23938 var editor= this.editor;
23941 var btn = function(id,cmd , toggle, handler, html){
23943 var event = toggle ? 'toggle' : 'click';
23948 xns: Roo.bootstrap,
23951 enableToggle:toggle !== false,
23953 pressed : toggle ? false : null,
23956 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23957 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23963 // var cb_box = function...
23968 xns: Roo.bootstrap,
23969 glyphicon : 'font',
23973 xns: Roo.bootstrap,
23977 Roo.each(this.formats, function(f) {
23978 style.menu.items.push({
23980 xns: Roo.bootstrap,
23981 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23986 editorcore.insertTag(this.tagname);
23993 children.push(style);
23995 btn('bold',false,true);
23996 btn('italic',false,true);
23997 btn('align-left', 'justifyleft',true);
23998 btn('align-center', 'justifycenter',true);
23999 btn('align-right' , 'justifyright',true);
24000 btn('link', false, false, function(btn) {
24001 //Roo.log("create link?");
24002 var url = prompt(this.createLinkText, this.defaultLinkValue);
24003 if(url && url != 'http:/'+'/'){
24004 this.editorcore.relayCmd('createlink', url);
24007 btn('list','insertunorderedlist',true);
24008 btn('pencil', false,true, function(btn){
24010 this.toggleSourceEdit(btn.pressed);
24013 if (this.editor.btns.length > 0) {
24014 for (var i = 0; i<this.editor.btns.length; i++) {
24015 children.push(this.editor.btns[i]);
24023 xns: Roo.bootstrap,
24028 xns: Roo.bootstrap,
24033 cog.menu.items.push({
24035 xns: Roo.bootstrap,
24036 html : Clean styles,
24041 editorcore.insertTag(this.tagname);
24050 this.xtype = 'NavSimplebar';
24052 for(var i=0;i< children.length;i++) {
24054 this.buttons.add(this.addxtypeChild(children[i]));
24058 editor.on('editorevent', this.updateToolbar, this);
24060 onBtnClick : function(id)
24062 this.editorcore.relayCmd(id);
24063 this.editorcore.focus();
24067 * Protected method that will not generally be called directly. It triggers
24068 * a toolbar update by reading the markup state of the current selection in the editor.
24070 updateToolbar: function(){
24072 if(!this.editorcore.activated){
24073 this.editor.onFirstFocus(); // is this neeed?
24077 var btns = this.buttons;
24078 var doc = this.editorcore.doc;
24079 btns.get('bold').setActive(doc.queryCommandState('bold'));
24080 btns.get('italic').setActive(doc.queryCommandState('italic'));
24081 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24083 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24084 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24085 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24087 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24088 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24091 var ans = this.editorcore.getAllAncestors();
24092 if (this.formatCombo) {
24095 var store = this.formatCombo.store;
24096 this.formatCombo.setValue("");
24097 for (var i =0; i < ans.length;i++) {
24098 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24100 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24108 // hides menus... - so this cant be on a menu...
24109 Roo.bootstrap.MenuMgr.hideAll();
24111 Roo.bootstrap.MenuMgr.hideAll();
24112 //this.editorsyncValue();
24114 onFirstFocus: function() {
24115 this.buttons.each(function(item){
24119 toggleSourceEdit : function(sourceEditMode){
24122 if(sourceEditMode){
24123 Roo.log("disabling buttons");
24124 this.buttons.each( function(item){
24125 if(item.cmd != 'pencil'){
24131 Roo.log("enabling buttons");
24132 if(this.editorcore.initialized){
24133 this.buttons.each( function(item){
24139 Roo.log("calling toggole on editor");
24140 // tell the editor that it's been pressed..
24141 this.editor.toggleSourceEdit(sourceEditMode);
24151 * @class Roo.bootstrap.Table.AbstractSelectionModel
24152 * @extends Roo.util.Observable
24153 * Abstract base class for grid SelectionModels. It provides the interface that should be
24154 * implemented by descendant classes. This class should not be directly instantiated.
24157 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24158 this.locked = false;
24159 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24163 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24164 /** @ignore Called by the grid automatically. Do not call directly. */
24165 init : function(grid){
24171 * Locks the selections.
24174 this.locked = true;
24178 * Unlocks the selections.
24180 unlock : function(){
24181 this.locked = false;
24185 * Returns true if the selections are locked.
24186 * @return {Boolean}
24188 isLocked : function(){
24189 return this.locked;
24193 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24194 * @class Roo.bootstrap.Table.RowSelectionModel
24195 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24196 * It supports multiple selections and keyboard selection/navigation.
24198 * @param {Object} config
24201 Roo.bootstrap.Table.RowSelectionModel = function(config){
24202 Roo.apply(this, config);
24203 this.selections = new Roo.util.MixedCollection(false, function(o){
24208 this.lastActive = false;
24212 * @event selectionchange
24213 * Fires when the selection changes
24214 * @param {SelectionModel} this
24216 "selectionchange" : true,
24218 * @event afterselectionchange
24219 * Fires after the selection changes (eg. by key press or clicking)
24220 * @param {SelectionModel} this
24222 "afterselectionchange" : true,
24224 * @event beforerowselect
24225 * Fires when a row is selected being selected, return false to cancel.
24226 * @param {SelectionModel} this
24227 * @param {Number} rowIndex The selected index
24228 * @param {Boolean} keepExisting False if other selections will be cleared
24230 "beforerowselect" : true,
24233 * Fires when a row is selected.
24234 * @param {SelectionModel} this
24235 * @param {Number} rowIndex The selected index
24236 * @param {Roo.data.Record} r The record
24238 "rowselect" : true,
24240 * @event rowdeselect
24241 * Fires when a row is deselected.
24242 * @param {SelectionModel} this
24243 * @param {Number} rowIndex The selected index
24245 "rowdeselect" : true
24247 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24248 this.locked = false;
24251 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24253 * @cfg {Boolean} singleSelect
24254 * True to allow selection of only one row at a time (defaults to false)
24256 singleSelect : false,
24259 initEvents : function()
24262 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24263 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24264 //}else{ // allow click to work like normal
24265 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24267 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24268 this.grid.on("rowclick", this.handleMouseDown, this);
24270 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24271 "up" : function(e){
24273 this.selectPrevious(e.shiftKey);
24274 }else if(this.last !== false && this.lastActive !== false){
24275 var last = this.last;
24276 this.selectRange(this.last, this.lastActive-1);
24277 this.grid.getView().focusRow(this.lastActive);
24278 if(last !== false){
24282 this.selectFirstRow();
24284 this.fireEvent("afterselectionchange", this);
24286 "down" : function(e){
24288 this.selectNext(e.shiftKey);
24289 }else if(this.last !== false && this.lastActive !== false){
24290 var last = this.last;
24291 this.selectRange(this.last, this.lastActive+1);
24292 this.grid.getView().focusRow(this.lastActive);
24293 if(last !== false){
24297 this.selectFirstRow();
24299 this.fireEvent("afterselectionchange", this);
24303 this.grid.store.on('load', function(){
24304 this.selections.clear();
24307 var view = this.grid.view;
24308 view.on("refresh", this.onRefresh, this);
24309 view.on("rowupdated", this.onRowUpdated, this);
24310 view.on("rowremoved", this.onRemove, this);
24315 onRefresh : function()
24317 var ds = this.grid.store, i, v = this.grid.view;
24318 var s = this.selections;
24319 s.each(function(r){
24320 if((i = ds.indexOfId(r.id)) != -1){
24329 onRemove : function(v, index, r){
24330 this.selections.remove(r);
24334 onRowUpdated : function(v, index, r){
24335 if(this.isSelected(r)){
24336 v.onRowSelect(index);
24342 * @param {Array} records The records to select
24343 * @param {Boolean} keepExisting (optional) True to keep existing selections
24345 selectRecords : function(records, keepExisting)
24348 this.clearSelections();
24350 var ds = this.grid.store;
24351 for(var i = 0, len = records.length; i < len; i++){
24352 this.selectRow(ds.indexOf(records[i]), true);
24357 * Gets the number of selected rows.
24360 getCount : function(){
24361 return this.selections.length;
24365 * Selects the first row in the grid.
24367 selectFirstRow : function(){
24372 * Select the last row.
24373 * @param {Boolean} keepExisting (optional) True to keep existing selections
24375 selectLastRow : function(keepExisting){
24376 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24377 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24381 * Selects the row immediately following the last selected row.
24382 * @param {Boolean} keepExisting (optional) True to keep existing selections
24384 selectNext : function(keepExisting)
24386 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24387 this.selectRow(this.last+1, keepExisting);
24388 this.grid.getView().focusRow(this.last);
24393 * Selects the row that precedes the last selected row.
24394 * @param {Boolean} keepExisting (optional) True to keep existing selections
24396 selectPrevious : function(keepExisting){
24398 this.selectRow(this.last-1, keepExisting);
24399 this.grid.getView().focusRow(this.last);
24404 * Returns the selected records
24405 * @return {Array} Array of selected records
24407 getSelections : function(){
24408 return [].concat(this.selections.items);
24412 * Returns the first selected record.
24415 getSelected : function(){
24416 return this.selections.itemAt(0);
24421 * Clears all selections.
24423 clearSelections : function(fast)
24429 var ds = this.grid.store;
24430 var s = this.selections;
24431 s.each(function(r){
24432 this.deselectRow(ds.indexOfId(r.id));
24436 this.selections.clear();
24443 * Selects all rows.
24445 selectAll : function(){
24449 this.selections.clear();
24450 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24451 this.selectRow(i, true);
24456 * Returns True if there is a selection.
24457 * @return {Boolean}
24459 hasSelection : function(){
24460 return this.selections.length > 0;
24464 * Returns True if the specified row is selected.
24465 * @param {Number/Record} record The record or index of the record to check
24466 * @return {Boolean}
24468 isSelected : function(index){
24469 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24470 return (r && this.selections.key(r.id) ? true : false);
24474 * Returns True if the specified record id is selected.
24475 * @param {String} id The id of record to check
24476 * @return {Boolean}
24478 isIdSelected : function(id){
24479 return (this.selections.key(id) ? true : false);
24484 handleMouseDBClick : function(e, t){
24488 handleMouseDown : function(e, t)
24490 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24491 if(this.isLocked() || rowIndex < 0 ){
24494 if(e.shiftKey && this.last !== false){
24495 var last = this.last;
24496 this.selectRange(last, rowIndex, e.ctrlKey);
24497 this.last = last; // reset the last
24501 var isSelected = this.isSelected(rowIndex);
24502 //Roo.log("select row:" + rowIndex);
24504 this.deselectRow(rowIndex);
24506 this.selectRow(rowIndex, true);
24510 if(e.button !== 0 && isSelected){
24511 alert('rowIndex 2: ' + rowIndex);
24512 view.focusRow(rowIndex);
24513 }else if(e.ctrlKey && isSelected){
24514 this.deselectRow(rowIndex);
24515 }else if(!isSelected){
24516 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24517 view.focusRow(rowIndex);
24521 this.fireEvent("afterselectionchange", this);
24524 handleDragableRowClick : function(grid, rowIndex, e)
24526 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24527 this.selectRow(rowIndex, false);
24528 grid.view.focusRow(rowIndex);
24529 this.fireEvent("afterselectionchange", this);
24534 * Selects multiple rows.
24535 * @param {Array} rows Array of the indexes of the row to select
24536 * @param {Boolean} keepExisting (optional) True to keep existing selections
24538 selectRows : function(rows, keepExisting){
24540 this.clearSelections();
24542 for(var i = 0, len = rows.length; i < len; i++){
24543 this.selectRow(rows[i], true);
24548 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24549 * @param {Number} startRow The index of the first row in the range
24550 * @param {Number} endRow The index of the last row in the range
24551 * @param {Boolean} keepExisting (optional) True to retain existing selections
24553 selectRange : function(startRow, endRow, keepExisting){
24558 this.clearSelections();
24560 if(startRow <= endRow){
24561 for(var i = startRow; i <= endRow; i++){
24562 this.selectRow(i, true);
24565 for(var i = startRow; i >= endRow; i--){
24566 this.selectRow(i, true);
24572 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24573 * @param {Number} startRow The index of the first row in the range
24574 * @param {Number} endRow The index of the last row in the range
24576 deselectRange : function(startRow, endRow, preventViewNotify){
24580 for(var i = startRow; i <= endRow; i++){
24581 this.deselectRow(i, preventViewNotify);
24587 * @param {Number} row The index of the row to select
24588 * @param {Boolean} keepExisting (optional) True to keep existing selections
24590 selectRow : function(index, keepExisting, preventViewNotify)
24592 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24595 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24596 if(!keepExisting || this.singleSelect){
24597 this.clearSelections();
24600 var r = this.grid.store.getAt(index);
24601 //console.log('selectRow - record id :' + r.id);
24603 this.selections.add(r);
24604 this.last = this.lastActive = index;
24605 if(!preventViewNotify){
24606 var proxy = new Roo.Element(
24607 this.grid.getRowDom(index)
24609 proxy.addClass('bg-info info');
24611 this.fireEvent("rowselect", this, index, r);
24612 this.fireEvent("selectionchange", this);
24618 * @param {Number} row The index of the row to deselect
24620 deselectRow : function(index, preventViewNotify)
24625 if(this.last == index){
24628 if(this.lastActive == index){
24629 this.lastActive = false;
24632 var r = this.grid.store.getAt(index);
24637 this.selections.remove(r);
24638 //.console.log('deselectRow - record id :' + r.id);
24639 if(!preventViewNotify){
24641 var proxy = new Roo.Element(
24642 this.grid.getRowDom(index)
24644 proxy.removeClass('bg-info info');
24646 this.fireEvent("rowdeselect", this, index);
24647 this.fireEvent("selectionchange", this);
24651 restoreLast : function(){
24653 this.last = this._last;
24658 acceptsNav : function(row, col, cm){
24659 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24663 onEditorKey : function(field, e){
24664 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24669 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24671 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24673 }else if(k == e.ENTER && !e.ctrlKey){
24677 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24679 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24681 }else if(k == e.ESC){
24685 g.startEditing(newCell[0], newCell[1]);
24691 * Ext JS Library 1.1.1
24692 * Copyright(c) 2006-2007, Ext JS, LLC.
24694 * Originally Released Under LGPL - original licence link has changed is not relivant.
24697 * <script type="text/javascript">
24701 * @class Roo.bootstrap.PagingToolbar
24702 * @extends Roo.bootstrap.NavSimplebar
24703 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24705 * Create a new PagingToolbar
24706 * @param {Object} config The config object
24707 * @param {Roo.data.Store} store
24709 Roo.bootstrap.PagingToolbar = function(config)
24711 // old args format still supported... - xtype is prefered..
24712 // created from xtype...
24714 this.ds = config.dataSource;
24716 if (config.store && !this.ds) {
24717 this.store= Roo.factory(config.store, Roo.data);
24718 this.ds = this.store;
24719 this.ds.xmodule = this.xmodule || false;
24722 this.toolbarItems = [];
24723 if (config.items) {
24724 this.toolbarItems = config.items;
24727 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24732 this.bind(this.ds);
24735 if (Roo.bootstrap.version == 4) {
24736 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24738 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24743 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24745 * @cfg {Roo.data.Store} dataSource
24746 * The underlying data store providing the paged data
24749 * @cfg {String/HTMLElement/Element} container
24750 * container The id or element that will contain the toolbar
24753 * @cfg {Boolean} displayInfo
24754 * True to display the displayMsg (defaults to false)
24757 * @cfg {Number} pageSize
24758 * The number of records to display per page (defaults to 20)
24762 * @cfg {String} displayMsg
24763 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24765 displayMsg : 'Displaying {0} - {1} of {2}',
24767 * @cfg {String} emptyMsg
24768 * The message to display when no records are found (defaults to "No data to display")
24770 emptyMsg : 'No data to display',
24772 * Customizable piece of the default paging text (defaults to "Page")
24775 beforePageText : "Page",
24777 * Customizable piece of the default paging text (defaults to "of %0")
24780 afterPageText : "of {0}",
24782 * Customizable piece of the default paging text (defaults to "First Page")
24785 firstText : "First Page",
24787 * Customizable piece of the default paging text (defaults to "Previous Page")
24790 prevText : "Previous Page",
24792 * Customizable piece of the default paging text (defaults to "Next Page")
24795 nextText : "Next Page",
24797 * Customizable piece of the default paging text (defaults to "Last Page")
24800 lastText : "Last Page",
24802 * Customizable piece of the default paging text (defaults to "Refresh")
24805 refreshText : "Refresh",
24809 onRender : function(ct, position)
24811 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24812 this.navgroup.parentId = this.id;
24813 this.navgroup.onRender(this.el, null);
24814 // add the buttons to the navgroup
24816 if(this.displayInfo){
24817 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24818 this.displayEl = this.el.select('.x-paging-info', true).first();
24819 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24820 // this.displayEl = navel.el.select('span',true).first();
24826 Roo.each(_this.buttons, function(e){ // this might need to use render????
24827 Roo.factory(e).render(_this.el);
24831 Roo.each(_this.toolbarItems, function(e) {
24832 _this.navgroup.addItem(e);
24836 this.first = this.navgroup.addItem({
24837 tooltip: this.firstText,
24838 cls: "prev btn-outline-secondary",
24839 html : ' <i class="fa fa-step-backward"></i>',
24841 preventDefault: true,
24842 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24845 this.prev = this.navgroup.addItem({
24846 tooltip: this.prevText,
24847 cls: "prev btn-outline-secondary",
24848 html : ' <i class="fa fa-backward"></i>',
24850 preventDefault: true,
24851 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24853 //this.addSeparator();
24856 var field = this.navgroup.addItem( {
24858 cls : 'x-paging-position btn-outline-secondary',
24860 html : this.beforePageText +
24861 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24862 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24865 this.field = field.el.select('input', true).first();
24866 this.field.on("keydown", this.onPagingKeydown, this);
24867 this.field.on("focus", function(){this.dom.select();});
24870 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24871 //this.field.setHeight(18);
24872 //this.addSeparator();
24873 this.next = this.navgroup.addItem({
24874 tooltip: this.nextText,
24875 cls: "next btn-outline-secondary",
24876 html : ' <i class="fa fa-forward"></i>',
24878 preventDefault: true,
24879 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24881 this.last = this.navgroup.addItem({
24882 tooltip: this.lastText,
24883 html : ' <i class="fa fa-step-forward"></i>',
24884 cls: "next btn-outline-secondary",
24886 preventDefault: true,
24887 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24889 //this.addSeparator();
24890 this.loading = this.navgroup.addItem({
24891 tooltip: this.refreshText,
24892 cls: "btn-outline-secondary",
24893 html : ' <i class="fa fa-refresh"></i>',
24894 preventDefault: true,
24895 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24901 updateInfo : function(){
24902 if(this.displayEl){
24903 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24904 var msg = count == 0 ?
24908 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24910 this.displayEl.update(msg);
24915 onLoad : function(ds, r, o)
24917 this.cursor = o.params.start ? o.params.start : 0;
24919 var d = this.getPageData(),
24924 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24925 this.field.dom.value = ap;
24926 this.first.setDisabled(ap == 1);
24927 this.prev.setDisabled(ap == 1);
24928 this.next.setDisabled(ap == ps);
24929 this.last.setDisabled(ap == ps);
24930 this.loading.enable();
24935 getPageData : function(){
24936 var total = this.ds.getTotalCount();
24939 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24940 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24945 onLoadError : function(){
24946 this.loading.enable();
24950 onPagingKeydown : function(e){
24951 var k = e.getKey();
24952 var d = this.getPageData();
24954 var v = this.field.dom.value, pageNum;
24955 if(!v || isNaN(pageNum = parseInt(v, 10))){
24956 this.field.dom.value = d.activePage;
24959 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24960 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24963 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))
24965 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24966 this.field.dom.value = pageNum;
24967 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24970 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24972 var v = this.field.dom.value, pageNum;
24973 var increment = (e.shiftKey) ? 10 : 1;
24974 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24977 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24978 this.field.dom.value = d.activePage;
24981 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24983 this.field.dom.value = parseInt(v, 10) + increment;
24984 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24985 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24992 beforeLoad : function(){
24994 this.loading.disable();
24999 onClick : function(which){
25008 ds.load({params:{start: 0, limit: this.pageSize}});
25011 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25014 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25017 var total = ds.getTotalCount();
25018 var extra = total % this.pageSize;
25019 var lastStart = extra ? (total - extra) : total-this.pageSize;
25020 ds.load({params:{start: lastStart, limit: this.pageSize}});
25023 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25029 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25030 * @param {Roo.data.Store} store The data store to unbind
25032 unbind : function(ds){
25033 ds.un("beforeload", this.beforeLoad, this);
25034 ds.un("load", this.onLoad, this);
25035 ds.un("loadexception", this.onLoadError, this);
25036 ds.un("remove", this.updateInfo, this);
25037 ds.un("add", this.updateInfo, this);
25038 this.ds = undefined;
25042 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25043 * @param {Roo.data.Store} store The data store to bind
25045 bind : function(ds){
25046 ds.on("beforeload", this.beforeLoad, this);
25047 ds.on("load", this.onLoad, this);
25048 ds.on("loadexception", this.onLoadError, this);
25049 ds.on("remove", this.updateInfo, this);
25050 ds.on("add", this.updateInfo, this);
25061 * @class Roo.bootstrap.MessageBar
25062 * @extends Roo.bootstrap.Component
25063 * Bootstrap MessageBar class
25064 * @cfg {String} html contents of the MessageBar
25065 * @cfg {String} weight (info | success | warning | danger) default info
25066 * @cfg {String} beforeClass insert the bar before the given class
25067 * @cfg {Boolean} closable (true | false) default false
25068 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25071 * Create a new Element
25072 * @param {Object} config The config object
25075 Roo.bootstrap.MessageBar = function(config){
25076 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25079 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25085 beforeClass: 'bootstrap-sticky-wrap',
25087 getAutoCreate : function(){
25091 cls: 'alert alert-dismissable alert-' + this.weight,
25096 html: this.html || ''
25102 cfg.cls += ' alert-messages-fixed';
25116 onRender : function(ct, position)
25118 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25121 var cfg = Roo.apply({}, this.getAutoCreate());
25125 cfg.cls += ' ' + this.cls;
25128 cfg.style = this.style;
25130 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25132 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25135 this.el.select('>button.close').on('click', this.hide, this);
25141 if (!this.rendered) {
25147 this.fireEvent('show', this);
25153 if (!this.rendered) {
25159 this.fireEvent('hide', this);
25162 update : function()
25164 // var e = this.el.dom.firstChild;
25166 // if(this.closable){
25167 // e = e.nextSibling;
25170 // e.data = this.html || '';
25172 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25188 * @class Roo.bootstrap.Graph
25189 * @extends Roo.bootstrap.Component
25190 * Bootstrap Graph class
25194 @cfg {String} graphtype bar | vbar | pie
25195 @cfg {number} g_x coodinator | centre x (pie)
25196 @cfg {number} g_y coodinator | centre y (pie)
25197 @cfg {number} g_r radius (pie)
25198 @cfg {number} g_height height of the chart (respected by all elements in the set)
25199 @cfg {number} g_width width of the chart (respected by all elements in the set)
25200 @cfg {Object} title The title of the chart
25203 -opts (object) options for the chart
25205 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25206 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25208 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.
25209 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25211 o stretch (boolean)
25213 -opts (object) options for the pie
25216 o startAngle (number)
25217 o endAngle (number)
25221 * Create a new Input
25222 * @param {Object} config The config object
25225 Roo.bootstrap.Graph = function(config){
25226 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25232 * The img click event for the img.
25233 * @param {Roo.EventObject} e
25239 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25250 //g_colors: this.colors,
25257 getAutoCreate : function(){
25268 onRender : function(ct,position){
25271 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25273 if (typeof(Raphael) == 'undefined') {
25274 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25278 this.raphael = Raphael(this.el.dom);
25280 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25281 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25282 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25283 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25285 r.text(160, 10, "Single Series Chart").attr(txtattr);
25286 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25287 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25288 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25290 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25291 r.barchart(330, 10, 300, 220, data1);
25292 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25293 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25296 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25297 // r.barchart(30, 30, 560, 250, xdata, {
25298 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25299 // axis : "0 0 1 1",
25300 // axisxlabels : xdata
25301 // //yvalues : cols,
25304 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25306 // this.load(null,xdata,{
25307 // axis : "0 0 1 1",
25308 // axisxlabels : xdata
25313 load : function(graphtype,xdata,opts)
25315 this.raphael.clear();
25317 graphtype = this.graphtype;
25322 var r = this.raphael,
25323 fin = function () {
25324 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25326 fout = function () {
25327 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25329 pfin = function() {
25330 this.sector.stop();
25331 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25334 this.label[0].stop();
25335 this.label[0].attr({ r: 7.5 });
25336 this.label[1].attr({ "font-weight": 800 });
25339 pfout = function() {
25340 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25343 this.label[0].animate({ r: 5 }, 500, "bounce");
25344 this.label[1].attr({ "font-weight": 400 });
25350 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25353 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25356 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25357 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25359 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25366 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25371 setTitle: function(o)
25376 initEvents: function() {
25379 this.el.on('click', this.onClick, this);
25383 onClick : function(e)
25385 Roo.log('img onclick');
25386 this.fireEvent('click', this, e);
25398 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25401 * @class Roo.bootstrap.dash.NumberBox
25402 * @extends Roo.bootstrap.Component
25403 * Bootstrap NumberBox class
25404 * @cfg {String} headline Box headline
25405 * @cfg {String} content Box content
25406 * @cfg {String} icon Box icon
25407 * @cfg {String} footer Footer text
25408 * @cfg {String} fhref Footer href
25411 * Create a new NumberBox
25412 * @param {Object} config The config object
25416 Roo.bootstrap.dash.NumberBox = function(config){
25417 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25421 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25430 getAutoCreate : function(){
25434 cls : 'small-box ',
25442 cls : 'roo-headline',
25443 html : this.headline
25447 cls : 'roo-content',
25448 html : this.content
25462 cls : 'ion ' + this.icon
25471 cls : 'small-box-footer',
25472 href : this.fhref || '#',
25476 cfg.cn.push(footer);
25483 onRender : function(ct,position){
25484 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25491 setHeadline: function (value)
25493 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25496 setFooter: function (value, href)
25498 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25501 this.el.select('a.small-box-footer',true).first().attr('href', href);
25506 setContent: function (value)
25508 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25511 initEvents: function()
25525 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25528 * @class Roo.bootstrap.dash.TabBox
25529 * @extends Roo.bootstrap.Component
25530 * Bootstrap TabBox class
25531 * @cfg {String} title Title of the TabBox
25532 * @cfg {String} icon Icon of the TabBox
25533 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25534 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25537 * Create a new TabBox
25538 * @param {Object} config The config object
25542 Roo.bootstrap.dash.TabBox = function(config){
25543 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25548 * When a pane is added
25549 * @param {Roo.bootstrap.dash.TabPane} pane
25553 * @event activatepane
25554 * When a pane is activated
25555 * @param {Roo.bootstrap.dash.TabPane} pane
25557 "activatepane" : true
25565 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25570 tabScrollable : false,
25572 getChildContainer : function()
25574 return this.el.select('.tab-content', true).first();
25577 getAutoCreate : function(){
25581 cls: 'pull-left header',
25589 cls: 'fa ' + this.icon
25595 cls: 'nav nav-tabs pull-right',
25601 if(this.tabScrollable){
25608 cls: 'nav nav-tabs pull-right',
25619 cls: 'nav-tabs-custom',
25624 cls: 'tab-content no-padding',
25632 initEvents : function()
25634 //Roo.log('add add pane handler');
25635 this.on('addpane', this.onAddPane, this);
25638 * Updates the box title
25639 * @param {String} html to set the title to.
25641 setTitle : function(value)
25643 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25645 onAddPane : function(pane)
25647 this.panes.push(pane);
25648 //Roo.log('addpane');
25650 // tabs are rendere left to right..
25651 if(!this.showtabs){
25655 var ctr = this.el.select('.nav-tabs', true).first();
25658 var existing = ctr.select('.nav-tab',true);
25659 var qty = existing.getCount();;
25662 var tab = ctr.createChild({
25664 cls : 'nav-tab' + (qty ? '' : ' active'),
25672 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25675 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25677 pane.el.addClass('active');
25682 onTabClick : function(ev,un,ob,pane)
25684 //Roo.log('tab - prev default');
25685 ev.preventDefault();
25688 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25689 pane.tab.addClass('active');
25690 //Roo.log(pane.title);
25691 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25692 // technically we should have a deactivate event.. but maybe add later.
25693 // and it should not de-activate the selected tab...
25694 this.fireEvent('activatepane', pane);
25695 pane.el.addClass('active');
25696 pane.fireEvent('activate');
25701 getActivePane : function()
25704 Roo.each(this.panes, function(p) {
25705 if(p.el.hasClass('active')){
25726 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25728 * @class Roo.bootstrap.TabPane
25729 * @extends Roo.bootstrap.Component
25730 * Bootstrap TabPane class
25731 * @cfg {Boolean} active (false | true) Default false
25732 * @cfg {String} title title of panel
25736 * Create a new TabPane
25737 * @param {Object} config The config object
25740 Roo.bootstrap.dash.TabPane = function(config){
25741 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25747 * When a pane is activated
25748 * @param {Roo.bootstrap.dash.TabPane} pane
25755 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25760 // the tabBox that this is attached to.
25763 getAutoCreate : function()
25771 cfg.cls += ' active';
25776 initEvents : function()
25778 //Roo.log('trigger add pane handler');
25779 this.parent().fireEvent('addpane', this)
25783 * Updates the tab title
25784 * @param {String} html to set the title to.
25786 setTitle: function(str)
25792 this.tab.select('a', true).first().dom.innerHTML = str;
25809 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25812 * @class Roo.bootstrap.menu.Menu
25813 * @extends Roo.bootstrap.Component
25814 * Bootstrap Menu class - container for Menu
25815 * @cfg {String} html Text of the menu
25816 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25817 * @cfg {String} icon Font awesome icon
25818 * @cfg {String} pos Menu align to (top | bottom) default bottom
25822 * Create a new Menu
25823 * @param {Object} config The config object
25827 Roo.bootstrap.menu.Menu = function(config){
25828 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25832 * @event beforeshow
25833 * Fires before this menu is displayed
25834 * @param {Roo.bootstrap.menu.Menu} this
25838 * @event beforehide
25839 * Fires before this menu is hidden
25840 * @param {Roo.bootstrap.menu.Menu} this
25845 * Fires after this menu is displayed
25846 * @param {Roo.bootstrap.menu.Menu} this
25851 * Fires after this menu is hidden
25852 * @param {Roo.bootstrap.menu.Menu} this
25857 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25858 * @param {Roo.bootstrap.menu.Menu} this
25859 * @param {Roo.EventObject} e
25866 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25870 weight : 'default',
25875 getChildContainer : function() {
25876 if(this.isSubMenu){
25880 return this.el.select('ul.dropdown-menu', true).first();
25883 getAutoCreate : function()
25888 cls : 'roo-menu-text',
25896 cls : 'fa ' + this.icon
25907 cls : 'dropdown-button btn btn-' + this.weight,
25912 cls : 'dropdown-toggle btn btn-' + this.weight,
25922 cls : 'dropdown-menu'
25928 if(this.pos == 'top'){
25929 cfg.cls += ' dropup';
25932 if(this.isSubMenu){
25935 cls : 'dropdown-menu'
25942 onRender : function(ct, position)
25944 this.isSubMenu = ct.hasClass('dropdown-submenu');
25946 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25949 initEvents : function()
25951 if(this.isSubMenu){
25955 this.hidden = true;
25957 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25958 this.triggerEl.on('click', this.onTriggerPress, this);
25960 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25961 this.buttonEl.on('click', this.onClick, this);
25967 if(this.isSubMenu){
25971 return this.el.select('ul.dropdown-menu', true).first();
25974 onClick : function(e)
25976 this.fireEvent("click", this, e);
25979 onTriggerPress : function(e)
25981 if (this.isVisible()) {
25988 isVisible : function(){
25989 return !this.hidden;
25994 this.fireEvent("beforeshow", this);
25996 this.hidden = false;
25997 this.el.addClass('open');
25999 Roo.get(document).on("mouseup", this.onMouseUp, this);
26001 this.fireEvent("show", this);
26008 this.fireEvent("beforehide", this);
26010 this.hidden = true;
26011 this.el.removeClass('open');
26013 Roo.get(document).un("mouseup", this.onMouseUp);
26015 this.fireEvent("hide", this);
26018 onMouseUp : function()
26032 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26035 * @class Roo.bootstrap.menu.Item
26036 * @extends Roo.bootstrap.Component
26037 * Bootstrap MenuItem class
26038 * @cfg {Boolean} submenu (true | false) default false
26039 * @cfg {String} html text of the item
26040 * @cfg {String} href the link
26041 * @cfg {Boolean} disable (true | false) default false
26042 * @cfg {Boolean} preventDefault (true | false) default true
26043 * @cfg {String} icon Font awesome icon
26044 * @cfg {String} pos Submenu align to (left | right) default right
26048 * Create a new Item
26049 * @param {Object} config The config object
26053 Roo.bootstrap.menu.Item = function(config){
26054 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26058 * Fires when the mouse is hovering over this menu
26059 * @param {Roo.bootstrap.menu.Item} this
26060 * @param {Roo.EventObject} e
26065 * Fires when the mouse exits this menu
26066 * @param {Roo.bootstrap.menu.Item} this
26067 * @param {Roo.EventObject} e
26073 * The raw click event for the entire grid.
26074 * @param {Roo.EventObject} e
26080 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26085 preventDefault: true,
26090 getAutoCreate : function()
26095 cls : 'roo-menu-item-text',
26103 cls : 'fa ' + this.icon
26112 href : this.href || '#',
26119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26123 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26125 if(this.pos == 'left'){
26126 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26133 initEvents : function()
26135 this.el.on('mouseover', this.onMouseOver, this);
26136 this.el.on('mouseout', this.onMouseOut, this);
26138 this.el.select('a', true).first().on('click', this.onClick, this);
26142 onClick : function(e)
26144 if(this.preventDefault){
26145 e.preventDefault();
26148 this.fireEvent("click", this, e);
26151 onMouseOver : function(e)
26153 if(this.submenu && this.pos == 'left'){
26154 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26157 this.fireEvent("mouseover", this, e);
26160 onMouseOut : function(e)
26162 this.fireEvent("mouseout", this, e);
26174 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26177 * @class Roo.bootstrap.menu.Separator
26178 * @extends Roo.bootstrap.Component
26179 * Bootstrap Separator class
26182 * Create a new Separator
26183 * @param {Object} config The config object
26187 Roo.bootstrap.menu.Separator = function(config){
26188 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26191 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26193 getAutoCreate : function(){
26214 * @class Roo.bootstrap.Tooltip
26215 * Bootstrap Tooltip class
26216 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26217 * to determine which dom element triggers the tooltip.
26219 * It needs to add support for additional attributes like tooltip-position
26222 * Create a new Toolti
26223 * @param {Object} config The config object
26226 Roo.bootstrap.Tooltip = function(config){
26227 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26229 this.alignment = Roo.bootstrap.Tooltip.alignment;
26231 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26232 this.alignment = config.alignment;
26237 Roo.apply(Roo.bootstrap.Tooltip, {
26239 * @function init initialize tooltip monitoring.
26243 currentTip : false,
26244 currentRegion : false,
26250 Roo.get(document).on('mouseover', this.enter ,this);
26251 Roo.get(document).on('mouseout', this.leave, this);
26254 this.currentTip = new Roo.bootstrap.Tooltip();
26257 enter : function(ev)
26259 var dom = ev.getTarget();
26261 //Roo.log(['enter',dom]);
26262 var el = Roo.fly(dom);
26263 if (this.currentEl) {
26265 //Roo.log(this.currentEl);
26266 //Roo.log(this.currentEl.contains(dom));
26267 if (this.currentEl == el) {
26270 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26276 if (this.currentTip.el) {
26277 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26281 if(!el || el.dom == document){
26287 // you can not look for children, as if el is the body.. then everythign is the child..
26288 if (!el.attr('tooltip')) { //
26289 if (!el.select("[tooltip]").elements.length) {
26292 // is the mouse over this child...?
26293 bindEl = el.select("[tooltip]").first();
26294 var xy = ev.getXY();
26295 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26296 //Roo.log("not in region.");
26299 //Roo.log("child element over..");
26302 this.currentEl = bindEl;
26303 this.currentTip.bind(bindEl);
26304 this.currentRegion = Roo.lib.Region.getRegion(dom);
26305 this.currentTip.enter();
26308 leave : function(ev)
26310 var dom = ev.getTarget();
26311 //Roo.log(['leave',dom]);
26312 if (!this.currentEl) {
26317 if (dom != this.currentEl.dom) {
26320 var xy = ev.getXY();
26321 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26324 // only activate leave if mouse cursor is outside... bounding box..
26329 if (this.currentTip) {
26330 this.currentTip.leave();
26332 //Roo.log('clear currentEl');
26333 this.currentEl = false;
26338 'left' : ['r-l', [-2,0], 'right'],
26339 'right' : ['l-r', [2,0], 'left'],
26340 'bottom' : ['t-b', [0,2], 'top'],
26341 'top' : [ 'b-t', [0,-2], 'bottom']
26347 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26352 delay : null, // can be { show : 300 , hide: 500}
26356 hoverState : null, //???
26358 placement : 'bottom',
26362 getAutoCreate : function(){
26369 cls : 'tooltip-arrow'
26372 cls : 'tooltip-inner'
26379 bind : function(el)
26385 enter : function () {
26387 if (this.timeout != null) {
26388 clearTimeout(this.timeout);
26391 this.hoverState = 'in';
26392 //Roo.log("enter - show");
26393 if (!this.delay || !this.delay.show) {
26398 this.timeout = setTimeout(function () {
26399 if (_t.hoverState == 'in') {
26402 }, this.delay.show);
26406 clearTimeout(this.timeout);
26408 this.hoverState = 'out';
26409 if (!this.delay || !this.delay.hide) {
26415 this.timeout = setTimeout(function () {
26416 //Roo.log("leave - timeout");
26418 if (_t.hoverState == 'out') {
26420 Roo.bootstrap.Tooltip.currentEl = false;
26425 show : function (msg)
26428 this.render(document.body);
26431 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26433 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26435 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26437 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26439 var placement = typeof this.placement == 'function' ?
26440 this.placement.call(this, this.el, on_el) :
26443 var autoToken = /\s?auto?\s?/i;
26444 var autoPlace = autoToken.test(placement);
26446 placement = placement.replace(autoToken, '') || 'top';
26450 //this.el.setXY([0,0]);
26452 //this.el.dom.style.display='block';
26454 //this.el.appendTo(on_el);
26456 var p = this.getPosition();
26457 var box = this.el.getBox();
26463 var align = this.alignment[placement];
26465 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26467 if(placement == 'top' || placement == 'bottom'){
26469 placement = 'right';
26472 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26473 placement = 'left';
26476 var scroll = Roo.select('body', true).first().getScroll();
26478 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26482 align = this.alignment[placement];
26485 this.el.alignTo(this.bindEl, align[0],align[1]);
26486 //var arrow = this.el.select('.arrow',true).first();
26487 //arrow.set(align[2],
26489 this.el.addClass(placement);
26491 this.el.addClass('in fade');
26493 this.hoverState = null;
26495 if (this.el.hasClass('fade')) {
26506 //this.el.setXY([0,0]);
26507 this.el.removeClass('in');
26523 * @class Roo.bootstrap.LocationPicker
26524 * @extends Roo.bootstrap.Component
26525 * Bootstrap LocationPicker class
26526 * @cfg {Number} latitude Position when init default 0
26527 * @cfg {Number} longitude Position when init default 0
26528 * @cfg {Number} zoom default 15
26529 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26530 * @cfg {Boolean} mapTypeControl default false
26531 * @cfg {Boolean} disableDoubleClickZoom default false
26532 * @cfg {Boolean} scrollwheel default true
26533 * @cfg {Boolean} streetViewControl default false
26534 * @cfg {Number} radius default 0
26535 * @cfg {String} locationName
26536 * @cfg {Boolean} draggable default true
26537 * @cfg {Boolean} enableAutocomplete default false
26538 * @cfg {Boolean} enableReverseGeocode default true
26539 * @cfg {String} markerTitle
26542 * Create a new LocationPicker
26543 * @param {Object} config The config object
26547 Roo.bootstrap.LocationPicker = function(config){
26549 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26554 * Fires when the picker initialized.
26555 * @param {Roo.bootstrap.LocationPicker} this
26556 * @param {Google Location} location
26560 * @event positionchanged
26561 * Fires when the picker position changed.
26562 * @param {Roo.bootstrap.LocationPicker} this
26563 * @param {Google Location} location
26565 positionchanged : true,
26568 * Fires when the map resize.
26569 * @param {Roo.bootstrap.LocationPicker} this
26574 * Fires when the map show.
26575 * @param {Roo.bootstrap.LocationPicker} this
26580 * Fires when the map hide.
26581 * @param {Roo.bootstrap.LocationPicker} this
26586 * Fires when click the map.
26587 * @param {Roo.bootstrap.LocationPicker} this
26588 * @param {Map event} e
26592 * @event mapRightClick
26593 * Fires when right click the map.
26594 * @param {Roo.bootstrap.LocationPicker} this
26595 * @param {Map event} e
26597 mapRightClick : true,
26599 * @event markerClick
26600 * Fires when click the marker.
26601 * @param {Roo.bootstrap.LocationPicker} this
26602 * @param {Map event} e
26604 markerClick : true,
26606 * @event markerRightClick
26607 * Fires when right click the marker.
26608 * @param {Roo.bootstrap.LocationPicker} this
26609 * @param {Map event} e
26611 markerRightClick : true,
26613 * @event OverlayViewDraw
26614 * Fires when OverlayView Draw
26615 * @param {Roo.bootstrap.LocationPicker} this
26617 OverlayViewDraw : true,
26619 * @event OverlayViewOnAdd
26620 * Fires when OverlayView Draw
26621 * @param {Roo.bootstrap.LocationPicker} this
26623 OverlayViewOnAdd : true,
26625 * @event OverlayViewOnRemove
26626 * Fires when OverlayView Draw
26627 * @param {Roo.bootstrap.LocationPicker} this
26629 OverlayViewOnRemove : true,
26631 * @event OverlayViewShow
26632 * Fires when OverlayView Draw
26633 * @param {Roo.bootstrap.LocationPicker} this
26634 * @param {Pixel} cpx
26636 OverlayViewShow : true,
26638 * @event OverlayViewHide
26639 * Fires when OverlayView Draw
26640 * @param {Roo.bootstrap.LocationPicker} this
26642 OverlayViewHide : true,
26644 * @event loadexception
26645 * Fires when load google lib failed.
26646 * @param {Roo.bootstrap.LocationPicker} this
26648 loadexception : true
26653 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26655 gMapContext: false,
26661 mapTypeControl: false,
26662 disableDoubleClickZoom: false,
26664 streetViewControl: false,
26668 enableAutocomplete: false,
26669 enableReverseGeocode: true,
26672 getAutoCreate: function()
26677 cls: 'roo-location-picker'
26683 initEvents: function(ct, position)
26685 if(!this.el.getWidth() || this.isApplied()){
26689 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26694 initial: function()
26696 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26697 this.fireEvent('loadexception', this);
26701 if(!this.mapTypeId){
26702 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26705 this.gMapContext = this.GMapContext();
26707 this.initOverlayView();
26709 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26713 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26714 _this.setPosition(_this.gMapContext.marker.position);
26717 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26718 _this.fireEvent('mapClick', this, event);
26722 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26723 _this.fireEvent('mapRightClick', this, event);
26727 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26728 _this.fireEvent('markerClick', this, event);
26732 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26733 _this.fireEvent('markerRightClick', this, event);
26737 this.setPosition(this.gMapContext.location);
26739 this.fireEvent('initial', this, this.gMapContext.location);
26742 initOverlayView: function()
26746 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26750 _this.fireEvent('OverlayViewDraw', _this);
26755 _this.fireEvent('OverlayViewOnAdd', _this);
26758 onRemove: function()
26760 _this.fireEvent('OverlayViewOnRemove', _this);
26763 show: function(cpx)
26765 _this.fireEvent('OverlayViewShow', _this, cpx);
26770 _this.fireEvent('OverlayViewHide', _this);
26776 fromLatLngToContainerPixel: function(event)
26778 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26781 isApplied: function()
26783 return this.getGmapContext() == false ? false : true;
26786 getGmapContext: function()
26788 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26791 GMapContext: function()
26793 var position = new google.maps.LatLng(this.latitude, this.longitude);
26795 var _map = new google.maps.Map(this.el.dom, {
26798 mapTypeId: this.mapTypeId,
26799 mapTypeControl: this.mapTypeControl,
26800 disableDoubleClickZoom: this.disableDoubleClickZoom,
26801 scrollwheel: this.scrollwheel,
26802 streetViewControl: this.streetViewControl,
26803 locationName: this.locationName,
26804 draggable: this.draggable,
26805 enableAutocomplete: this.enableAutocomplete,
26806 enableReverseGeocode: this.enableReverseGeocode
26809 var _marker = new google.maps.Marker({
26810 position: position,
26812 title: this.markerTitle,
26813 draggable: this.draggable
26820 location: position,
26821 radius: this.radius,
26822 locationName: this.locationName,
26823 addressComponents: {
26824 formatted_address: null,
26825 addressLine1: null,
26826 addressLine2: null,
26828 streetNumber: null,
26832 stateOrProvince: null
26835 domContainer: this.el.dom,
26836 geodecoder: new google.maps.Geocoder()
26840 drawCircle: function(center, radius, options)
26842 if (this.gMapContext.circle != null) {
26843 this.gMapContext.circle.setMap(null);
26847 options = Roo.apply({}, options, {
26848 strokeColor: "#0000FF",
26849 strokeOpacity: .35,
26851 fillColor: "#0000FF",
26855 options.map = this.gMapContext.map;
26856 options.radius = radius;
26857 options.center = center;
26858 this.gMapContext.circle = new google.maps.Circle(options);
26859 return this.gMapContext.circle;
26865 setPosition: function(location)
26867 this.gMapContext.location = location;
26868 this.gMapContext.marker.setPosition(location);
26869 this.gMapContext.map.panTo(location);
26870 this.drawCircle(location, this.gMapContext.radius, {});
26874 if (this.gMapContext.settings.enableReverseGeocode) {
26875 this.gMapContext.geodecoder.geocode({
26876 latLng: this.gMapContext.location
26877 }, function(results, status) {
26879 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26880 _this.gMapContext.locationName = results[0].formatted_address;
26881 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26883 _this.fireEvent('positionchanged', this, location);
26890 this.fireEvent('positionchanged', this, location);
26895 google.maps.event.trigger(this.gMapContext.map, "resize");
26897 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26899 this.fireEvent('resize', this);
26902 setPositionByLatLng: function(latitude, longitude)
26904 this.setPosition(new google.maps.LatLng(latitude, longitude));
26907 getCurrentPosition: function()
26910 latitude: this.gMapContext.location.lat(),
26911 longitude: this.gMapContext.location.lng()
26915 getAddressName: function()
26917 return this.gMapContext.locationName;
26920 getAddressComponents: function()
26922 return this.gMapContext.addressComponents;
26925 address_component_from_google_geocode: function(address_components)
26929 for (var i = 0; i < address_components.length; i++) {
26930 var component = address_components[i];
26931 if (component.types.indexOf("postal_code") >= 0) {
26932 result.postalCode = component.short_name;
26933 } else if (component.types.indexOf("street_number") >= 0) {
26934 result.streetNumber = component.short_name;
26935 } else if (component.types.indexOf("route") >= 0) {
26936 result.streetName = component.short_name;
26937 } else if (component.types.indexOf("neighborhood") >= 0) {
26938 result.city = component.short_name;
26939 } else if (component.types.indexOf("locality") >= 0) {
26940 result.city = component.short_name;
26941 } else if (component.types.indexOf("sublocality") >= 0) {
26942 result.district = component.short_name;
26943 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26944 result.stateOrProvince = component.short_name;
26945 } else if (component.types.indexOf("country") >= 0) {
26946 result.country = component.short_name;
26950 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26951 result.addressLine2 = "";
26955 setZoomLevel: function(zoom)
26957 this.gMapContext.map.setZoom(zoom);
26970 this.fireEvent('show', this);
26981 this.fireEvent('hide', this);
26986 Roo.apply(Roo.bootstrap.LocationPicker, {
26988 OverlayView : function(map, options)
26990 options = options || {};
27004 * @class Roo.bootstrap.Alert
27005 * @extends Roo.bootstrap.Component
27006 * Bootstrap Alert class
27007 * @cfg {String} title The title of alert
27008 * @cfg {String} html The content of alert
27009 * @cfg {String} weight ( success | info | warning | danger )
27010 * @cfg {String} faicon font-awesomeicon
27013 * Create a new alert
27014 * @param {Object} config The config object
27018 Roo.bootstrap.Alert = function(config){
27019 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27023 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27030 getAutoCreate : function()
27039 cls : 'roo-alert-icon'
27044 cls : 'roo-alert-title',
27049 cls : 'roo-alert-text',
27056 cfg.cn[0].cls += ' fa ' + this.faicon;
27060 cfg.cls += ' alert-' + this.weight;
27066 initEvents: function()
27068 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27071 setTitle : function(str)
27073 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27076 setText : function(str)
27078 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27081 setWeight : function(weight)
27084 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27087 this.weight = weight;
27089 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27092 setIcon : function(icon)
27095 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27098 this.faicon = icon;
27100 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27121 * @class Roo.bootstrap.UploadCropbox
27122 * @extends Roo.bootstrap.Component
27123 * Bootstrap UploadCropbox class
27124 * @cfg {String} emptyText show when image has been loaded
27125 * @cfg {String} rotateNotify show when image too small to rotate
27126 * @cfg {Number} errorTimeout default 3000
27127 * @cfg {Number} minWidth default 300
27128 * @cfg {Number} minHeight default 300
27129 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27130 * @cfg {Boolean} isDocument (true|false) default false
27131 * @cfg {String} url action url
27132 * @cfg {String} paramName default 'imageUpload'
27133 * @cfg {String} method default POST
27134 * @cfg {Boolean} loadMask (true|false) default true
27135 * @cfg {Boolean} loadingText default 'Loading...'
27138 * Create a new UploadCropbox
27139 * @param {Object} config The config object
27142 Roo.bootstrap.UploadCropbox = function(config){
27143 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27147 * @event beforeselectfile
27148 * Fire before select file
27149 * @param {Roo.bootstrap.UploadCropbox} this
27151 "beforeselectfile" : true,
27154 * Fire after initEvent
27155 * @param {Roo.bootstrap.UploadCropbox} this
27160 * Fire after initEvent
27161 * @param {Roo.bootstrap.UploadCropbox} this
27162 * @param {String} data
27167 * Fire when preparing the file data
27168 * @param {Roo.bootstrap.UploadCropbox} this
27169 * @param {Object} file
27174 * Fire when get exception
27175 * @param {Roo.bootstrap.UploadCropbox} this
27176 * @param {XMLHttpRequest} xhr
27178 "exception" : true,
27180 * @event beforeloadcanvas
27181 * Fire before load the canvas
27182 * @param {Roo.bootstrap.UploadCropbox} this
27183 * @param {String} src
27185 "beforeloadcanvas" : true,
27188 * Fire when trash image
27189 * @param {Roo.bootstrap.UploadCropbox} this
27194 * Fire when download the image
27195 * @param {Roo.bootstrap.UploadCropbox} this
27199 * @event footerbuttonclick
27200 * Fire when footerbuttonclick
27201 * @param {Roo.bootstrap.UploadCropbox} this
27202 * @param {String} type
27204 "footerbuttonclick" : true,
27208 * @param {Roo.bootstrap.UploadCropbox} this
27213 * Fire when rotate the image
27214 * @param {Roo.bootstrap.UploadCropbox} this
27215 * @param {String} pos
27220 * Fire when inspect the file
27221 * @param {Roo.bootstrap.UploadCropbox} this
27222 * @param {Object} file
27227 * Fire when xhr upload the file
27228 * @param {Roo.bootstrap.UploadCropbox} this
27229 * @param {Object} data
27234 * Fire when arrange the file data
27235 * @param {Roo.bootstrap.UploadCropbox} this
27236 * @param {Object} formData
27241 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27244 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27246 emptyText : 'Click to upload image',
27247 rotateNotify : 'Image is too small to rotate',
27248 errorTimeout : 3000,
27262 cropType : 'image/jpeg',
27264 canvasLoaded : false,
27265 isDocument : false,
27267 paramName : 'imageUpload',
27269 loadingText : 'Loading...',
27272 getAutoCreate : function()
27276 cls : 'roo-upload-cropbox',
27280 cls : 'roo-upload-cropbox-selector',
27285 cls : 'roo-upload-cropbox-body',
27286 style : 'cursor:pointer',
27290 cls : 'roo-upload-cropbox-preview'
27294 cls : 'roo-upload-cropbox-thumb'
27298 cls : 'roo-upload-cropbox-empty-notify',
27299 html : this.emptyText
27303 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27304 html : this.rotateNotify
27310 cls : 'roo-upload-cropbox-footer',
27313 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27323 onRender : function(ct, position)
27325 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27327 if (this.buttons.length) {
27329 Roo.each(this.buttons, function(bb) {
27331 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27333 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27339 this.maskEl = this.el;
27343 initEvents : function()
27345 this.urlAPI = (window.createObjectURL && window) ||
27346 (window.URL && URL.revokeObjectURL && URL) ||
27347 (window.webkitURL && webkitURL);
27349 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27350 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27352 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27353 this.selectorEl.hide();
27355 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27356 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27358 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27359 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27360 this.thumbEl.hide();
27362 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27363 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27365 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27366 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27367 this.errorEl.hide();
27369 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27370 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27371 this.footerEl.hide();
27373 this.setThumbBoxSize();
27379 this.fireEvent('initial', this);
27386 window.addEventListener("resize", function() { _this.resize(); } );
27388 this.bodyEl.on('click', this.beforeSelectFile, this);
27391 this.bodyEl.on('touchstart', this.onTouchStart, this);
27392 this.bodyEl.on('touchmove', this.onTouchMove, this);
27393 this.bodyEl.on('touchend', this.onTouchEnd, this);
27397 this.bodyEl.on('mousedown', this.onMouseDown, this);
27398 this.bodyEl.on('mousemove', this.onMouseMove, this);
27399 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27400 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27401 Roo.get(document).on('mouseup', this.onMouseUp, this);
27404 this.selectorEl.on('change', this.onFileSelected, this);
27410 this.baseScale = 1;
27412 this.baseRotate = 1;
27413 this.dragable = false;
27414 this.pinching = false;
27417 this.cropData = false;
27418 this.notifyEl.dom.innerHTML = this.emptyText;
27420 this.selectorEl.dom.value = '';
27424 resize : function()
27426 if(this.fireEvent('resize', this) != false){
27427 this.setThumbBoxPosition();
27428 this.setCanvasPosition();
27432 onFooterButtonClick : function(e, el, o, type)
27435 case 'rotate-left' :
27436 this.onRotateLeft(e);
27438 case 'rotate-right' :
27439 this.onRotateRight(e);
27442 this.beforeSelectFile(e);
27457 this.fireEvent('footerbuttonclick', this, type);
27460 beforeSelectFile : function(e)
27462 e.preventDefault();
27464 if(this.fireEvent('beforeselectfile', this) != false){
27465 this.selectorEl.dom.click();
27469 onFileSelected : function(e)
27471 e.preventDefault();
27473 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27477 var file = this.selectorEl.dom.files[0];
27479 if(this.fireEvent('inspect', this, file) != false){
27480 this.prepare(file);
27485 trash : function(e)
27487 this.fireEvent('trash', this);
27490 download : function(e)
27492 this.fireEvent('download', this);
27495 loadCanvas : function(src)
27497 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27501 this.imageEl = document.createElement('img');
27505 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27507 this.imageEl.src = src;
27511 onLoadCanvas : function()
27513 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27514 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27516 this.bodyEl.un('click', this.beforeSelectFile, this);
27518 this.notifyEl.hide();
27519 this.thumbEl.show();
27520 this.footerEl.show();
27522 this.baseRotateLevel();
27524 if(this.isDocument){
27525 this.setThumbBoxSize();
27528 this.setThumbBoxPosition();
27530 this.baseScaleLevel();
27536 this.canvasLoaded = true;
27539 this.maskEl.unmask();
27544 setCanvasPosition : function()
27546 if(!this.canvasEl){
27550 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27551 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27553 this.previewEl.setLeft(pw);
27554 this.previewEl.setTop(ph);
27558 onMouseDown : function(e)
27562 this.dragable = true;
27563 this.pinching = false;
27565 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27566 this.dragable = false;
27570 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27571 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27575 onMouseMove : function(e)
27579 if(!this.canvasLoaded){
27583 if (!this.dragable){
27587 var minX = Math.ceil(this.thumbEl.getLeft(true));
27588 var minY = Math.ceil(this.thumbEl.getTop(true));
27590 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27591 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27593 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27594 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27596 x = x - this.mouseX;
27597 y = y - this.mouseY;
27599 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27600 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27602 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27603 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27605 this.previewEl.setLeft(bgX);
27606 this.previewEl.setTop(bgY);
27608 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27609 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27612 onMouseUp : function(e)
27616 this.dragable = false;
27619 onMouseWheel : function(e)
27623 this.startScale = this.scale;
27625 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27627 if(!this.zoomable()){
27628 this.scale = this.startScale;
27637 zoomable : function()
27639 var minScale = this.thumbEl.getWidth() / this.minWidth;
27641 if(this.minWidth < this.minHeight){
27642 minScale = this.thumbEl.getHeight() / this.minHeight;
27645 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27646 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27650 (this.rotate == 0 || this.rotate == 180) &&
27652 width > this.imageEl.OriginWidth ||
27653 height > this.imageEl.OriginHeight ||
27654 (width < this.minWidth && height < this.minHeight)
27662 (this.rotate == 90 || this.rotate == 270) &&
27664 width > this.imageEl.OriginWidth ||
27665 height > this.imageEl.OriginHeight ||
27666 (width < this.minHeight && height < this.minWidth)
27673 !this.isDocument &&
27674 (this.rotate == 0 || this.rotate == 180) &&
27676 width < this.minWidth ||
27677 width > this.imageEl.OriginWidth ||
27678 height < this.minHeight ||
27679 height > this.imageEl.OriginHeight
27686 !this.isDocument &&
27687 (this.rotate == 90 || this.rotate == 270) &&
27689 width < this.minHeight ||
27690 width > this.imageEl.OriginWidth ||
27691 height < this.minWidth ||
27692 height > this.imageEl.OriginHeight
27702 onRotateLeft : function(e)
27704 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27706 var minScale = this.thumbEl.getWidth() / this.minWidth;
27708 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27709 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27711 this.startScale = this.scale;
27713 while (this.getScaleLevel() < minScale){
27715 this.scale = this.scale + 1;
27717 if(!this.zoomable()){
27722 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27723 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27728 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27735 this.scale = this.startScale;
27737 this.onRotateFail();
27742 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27744 if(this.isDocument){
27745 this.setThumbBoxSize();
27746 this.setThumbBoxPosition();
27747 this.setCanvasPosition();
27752 this.fireEvent('rotate', this, 'left');
27756 onRotateRight : function(e)
27758 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27760 var minScale = this.thumbEl.getWidth() / this.minWidth;
27762 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27763 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27765 this.startScale = this.scale;
27767 while (this.getScaleLevel() < minScale){
27769 this.scale = this.scale + 1;
27771 if(!this.zoomable()){
27776 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27777 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27782 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27789 this.scale = this.startScale;
27791 this.onRotateFail();
27796 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27798 if(this.isDocument){
27799 this.setThumbBoxSize();
27800 this.setThumbBoxPosition();
27801 this.setCanvasPosition();
27806 this.fireEvent('rotate', this, 'right');
27809 onRotateFail : function()
27811 this.errorEl.show(true);
27815 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27820 this.previewEl.dom.innerHTML = '';
27822 var canvasEl = document.createElement("canvas");
27824 var contextEl = canvasEl.getContext("2d");
27826 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27827 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27828 var center = this.imageEl.OriginWidth / 2;
27830 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27831 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27832 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27833 center = this.imageEl.OriginHeight / 2;
27836 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27838 contextEl.translate(center, center);
27839 contextEl.rotate(this.rotate * Math.PI / 180);
27841 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27843 this.canvasEl = document.createElement("canvas");
27845 this.contextEl = this.canvasEl.getContext("2d");
27847 switch (this.rotate) {
27850 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27851 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27853 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27858 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27859 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27861 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27862 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);
27866 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27871 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27872 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27874 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27875 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);
27879 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);
27884 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27885 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27887 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27888 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27892 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);
27899 this.previewEl.appendChild(this.canvasEl);
27901 this.setCanvasPosition();
27906 if(!this.canvasLoaded){
27910 var imageCanvas = document.createElement("canvas");
27912 var imageContext = imageCanvas.getContext("2d");
27914 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27915 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27917 var center = imageCanvas.width / 2;
27919 imageContext.translate(center, center);
27921 imageContext.rotate(this.rotate * Math.PI / 180);
27923 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27925 var canvas = document.createElement("canvas");
27927 var context = canvas.getContext("2d");
27929 canvas.width = this.minWidth;
27930 canvas.height = this.minHeight;
27932 switch (this.rotate) {
27935 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27936 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27938 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27939 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27941 var targetWidth = this.minWidth - 2 * x;
27942 var targetHeight = this.minHeight - 2 * y;
27946 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27947 scale = targetWidth / width;
27950 if(x > 0 && y == 0){
27951 scale = targetHeight / height;
27954 if(x > 0 && y > 0){
27955 scale = targetWidth / width;
27957 if(width < height){
27958 scale = targetHeight / height;
27962 context.scale(scale, scale);
27964 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27965 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27967 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27968 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27970 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27975 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27976 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27978 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27979 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27981 var targetWidth = this.minWidth - 2 * x;
27982 var targetHeight = this.minHeight - 2 * y;
27986 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27987 scale = targetWidth / width;
27990 if(x > 0 && y == 0){
27991 scale = targetHeight / height;
27994 if(x > 0 && y > 0){
27995 scale = targetWidth / width;
27997 if(width < height){
27998 scale = targetHeight / height;
28002 context.scale(scale, scale);
28004 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28005 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28007 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28008 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28010 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28012 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28017 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28018 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28020 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28021 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28023 var targetWidth = this.minWidth - 2 * x;
28024 var targetHeight = this.minHeight - 2 * y;
28028 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28029 scale = targetWidth / width;
28032 if(x > 0 && y == 0){
28033 scale = targetHeight / height;
28036 if(x > 0 && y > 0){
28037 scale = targetWidth / width;
28039 if(width < height){
28040 scale = targetHeight / height;
28044 context.scale(scale, scale);
28046 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28047 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28049 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28050 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28052 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28053 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28055 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28060 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28061 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28063 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28064 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28066 var targetWidth = this.minWidth - 2 * x;
28067 var targetHeight = this.minHeight - 2 * y;
28071 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28072 scale = targetWidth / width;
28075 if(x > 0 && y == 0){
28076 scale = targetHeight / height;
28079 if(x > 0 && y > 0){
28080 scale = targetWidth / width;
28082 if(width < height){
28083 scale = targetHeight / height;
28087 context.scale(scale, scale);
28089 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28090 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28092 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28093 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28095 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28097 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28104 this.cropData = canvas.toDataURL(this.cropType);
28106 if(this.fireEvent('crop', this, this.cropData) !== false){
28107 this.process(this.file, this.cropData);
28114 setThumbBoxSize : function()
28118 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28119 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28120 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28122 this.minWidth = width;
28123 this.minHeight = height;
28125 if(this.rotate == 90 || this.rotate == 270){
28126 this.minWidth = height;
28127 this.minHeight = width;
28132 width = Math.ceil(this.minWidth * height / this.minHeight);
28134 if(this.minWidth > this.minHeight){
28136 height = Math.ceil(this.minHeight * width / this.minWidth);
28139 this.thumbEl.setStyle({
28140 width : width + 'px',
28141 height : height + 'px'
28148 setThumbBoxPosition : function()
28150 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28151 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28153 this.thumbEl.setLeft(x);
28154 this.thumbEl.setTop(y);
28158 baseRotateLevel : function()
28160 this.baseRotate = 1;
28163 typeof(this.exif) != 'undefined' &&
28164 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28165 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28167 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28170 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28174 baseScaleLevel : function()
28178 if(this.isDocument){
28180 if(this.baseRotate == 6 || this.baseRotate == 8){
28182 height = this.thumbEl.getHeight();
28183 this.baseScale = height / this.imageEl.OriginWidth;
28185 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28186 width = this.thumbEl.getWidth();
28187 this.baseScale = width / this.imageEl.OriginHeight;
28193 height = this.thumbEl.getHeight();
28194 this.baseScale = height / this.imageEl.OriginHeight;
28196 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28197 width = this.thumbEl.getWidth();
28198 this.baseScale = width / this.imageEl.OriginWidth;
28204 if(this.baseRotate == 6 || this.baseRotate == 8){
28206 width = this.thumbEl.getHeight();
28207 this.baseScale = width / this.imageEl.OriginHeight;
28209 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28210 height = this.thumbEl.getWidth();
28211 this.baseScale = height / this.imageEl.OriginHeight;
28214 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28215 height = this.thumbEl.getWidth();
28216 this.baseScale = height / this.imageEl.OriginHeight;
28218 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28219 width = this.thumbEl.getHeight();
28220 this.baseScale = width / this.imageEl.OriginWidth;
28227 width = this.thumbEl.getWidth();
28228 this.baseScale = width / this.imageEl.OriginWidth;
28230 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28231 height = this.thumbEl.getHeight();
28232 this.baseScale = height / this.imageEl.OriginHeight;
28235 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28237 height = this.thumbEl.getHeight();
28238 this.baseScale = height / this.imageEl.OriginHeight;
28240 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28241 width = this.thumbEl.getWidth();
28242 this.baseScale = width / this.imageEl.OriginWidth;
28250 getScaleLevel : function()
28252 return this.baseScale * Math.pow(1.1, this.scale);
28255 onTouchStart : function(e)
28257 if(!this.canvasLoaded){
28258 this.beforeSelectFile(e);
28262 var touches = e.browserEvent.touches;
28268 if(touches.length == 1){
28269 this.onMouseDown(e);
28273 if(touches.length != 2){
28279 for(var i = 0, finger; finger = touches[i]; i++){
28280 coords.push(finger.pageX, finger.pageY);
28283 var x = Math.pow(coords[0] - coords[2], 2);
28284 var y = Math.pow(coords[1] - coords[3], 2);
28286 this.startDistance = Math.sqrt(x + y);
28288 this.startScale = this.scale;
28290 this.pinching = true;
28291 this.dragable = false;
28295 onTouchMove : function(e)
28297 if(!this.pinching && !this.dragable){
28301 var touches = e.browserEvent.touches;
28308 this.onMouseMove(e);
28314 for(var i = 0, finger; finger = touches[i]; i++){
28315 coords.push(finger.pageX, finger.pageY);
28318 var x = Math.pow(coords[0] - coords[2], 2);
28319 var y = Math.pow(coords[1] - coords[3], 2);
28321 this.endDistance = Math.sqrt(x + y);
28323 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28325 if(!this.zoomable()){
28326 this.scale = this.startScale;
28334 onTouchEnd : function(e)
28336 this.pinching = false;
28337 this.dragable = false;
28341 process : function(file, crop)
28344 this.maskEl.mask(this.loadingText);
28347 this.xhr = new XMLHttpRequest();
28349 file.xhr = this.xhr;
28351 this.xhr.open(this.method, this.url, true);
28354 "Accept": "application/json",
28355 "Cache-Control": "no-cache",
28356 "X-Requested-With": "XMLHttpRequest"
28359 for (var headerName in headers) {
28360 var headerValue = headers[headerName];
28362 this.xhr.setRequestHeader(headerName, headerValue);
28368 this.xhr.onload = function()
28370 _this.xhrOnLoad(_this.xhr);
28373 this.xhr.onerror = function()
28375 _this.xhrOnError(_this.xhr);
28378 var formData = new FormData();
28380 formData.append('returnHTML', 'NO');
28383 formData.append('crop', crop);
28386 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28387 formData.append(this.paramName, file, file.name);
28390 if(typeof(file.filename) != 'undefined'){
28391 formData.append('filename', file.filename);
28394 if(typeof(file.mimetype) != 'undefined'){
28395 formData.append('mimetype', file.mimetype);
28398 if(this.fireEvent('arrange', this, formData) != false){
28399 this.xhr.send(formData);
28403 xhrOnLoad : function(xhr)
28406 this.maskEl.unmask();
28409 if (xhr.readyState !== 4) {
28410 this.fireEvent('exception', this, xhr);
28414 var response = Roo.decode(xhr.responseText);
28416 if(!response.success){
28417 this.fireEvent('exception', this, xhr);
28421 var response = Roo.decode(xhr.responseText);
28423 this.fireEvent('upload', this, response);
28427 xhrOnError : function()
28430 this.maskEl.unmask();
28433 Roo.log('xhr on error');
28435 var response = Roo.decode(xhr.responseText);
28441 prepare : function(file)
28444 this.maskEl.mask(this.loadingText);
28450 if(typeof(file) === 'string'){
28451 this.loadCanvas(file);
28455 if(!file || !this.urlAPI){
28460 this.cropType = file.type;
28464 if(this.fireEvent('prepare', this, this.file) != false){
28466 var reader = new FileReader();
28468 reader.onload = function (e) {
28469 if (e.target.error) {
28470 Roo.log(e.target.error);
28474 var buffer = e.target.result,
28475 dataView = new DataView(buffer),
28477 maxOffset = dataView.byteLength - 4,
28481 if (dataView.getUint16(0) === 0xffd8) {
28482 while (offset < maxOffset) {
28483 markerBytes = dataView.getUint16(offset);
28485 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28486 markerLength = dataView.getUint16(offset + 2) + 2;
28487 if (offset + markerLength > dataView.byteLength) {
28488 Roo.log('Invalid meta data: Invalid segment size.');
28492 if(markerBytes == 0xffe1){
28493 _this.parseExifData(
28500 offset += markerLength;
28510 var url = _this.urlAPI.createObjectURL(_this.file);
28512 _this.loadCanvas(url);
28517 reader.readAsArrayBuffer(this.file);
28523 parseExifData : function(dataView, offset, length)
28525 var tiffOffset = offset + 10,
28529 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28530 // No Exif data, might be XMP data instead
28534 // Check for the ASCII code for "Exif" (0x45786966):
28535 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28536 // No Exif data, might be XMP data instead
28539 if (tiffOffset + 8 > dataView.byteLength) {
28540 Roo.log('Invalid Exif data: Invalid segment size.');
28543 // Check for the two null bytes:
28544 if (dataView.getUint16(offset + 8) !== 0x0000) {
28545 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28548 // Check the byte alignment:
28549 switch (dataView.getUint16(tiffOffset)) {
28551 littleEndian = true;
28554 littleEndian = false;
28557 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28560 // Check for the TIFF tag marker (0x002A):
28561 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28562 Roo.log('Invalid Exif data: Missing TIFF marker.');
28565 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28566 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28568 this.parseExifTags(
28571 tiffOffset + dirOffset,
28576 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28581 if (dirOffset + 6 > dataView.byteLength) {
28582 Roo.log('Invalid Exif data: Invalid directory offset.');
28585 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28586 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28587 if (dirEndOffset + 4 > dataView.byteLength) {
28588 Roo.log('Invalid Exif data: Invalid directory size.');
28591 for (i = 0; i < tagsNumber; i += 1) {
28595 dirOffset + 2 + 12 * i, // tag offset
28599 // Return the offset to the next directory:
28600 return dataView.getUint32(dirEndOffset, littleEndian);
28603 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28605 var tag = dataView.getUint16(offset, littleEndian);
28607 this.exif[tag] = this.getExifValue(
28611 dataView.getUint16(offset + 2, littleEndian), // tag type
28612 dataView.getUint32(offset + 4, littleEndian), // tag length
28617 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28619 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28628 Roo.log('Invalid Exif data: Invalid tag type.');
28632 tagSize = tagType.size * length;
28633 // Determine if the value is contained in the dataOffset bytes,
28634 // or if the value at the dataOffset is a pointer to the actual data:
28635 dataOffset = tagSize > 4 ?
28636 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28637 if (dataOffset + tagSize > dataView.byteLength) {
28638 Roo.log('Invalid Exif data: Invalid data offset.');
28641 if (length === 1) {
28642 return tagType.getValue(dataView, dataOffset, littleEndian);
28645 for (i = 0; i < length; i += 1) {
28646 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28649 if (tagType.ascii) {
28651 // Concatenate the chars:
28652 for (i = 0; i < values.length; i += 1) {
28654 // Ignore the terminating NULL byte(s):
28655 if (c === '\u0000') {
28667 Roo.apply(Roo.bootstrap.UploadCropbox, {
28669 'Orientation': 0x0112
28673 1: 0, //'top-left',
28675 3: 180, //'bottom-right',
28676 // 4: 'bottom-left',
28678 6: 90, //'right-top',
28679 // 7: 'right-bottom',
28680 8: 270 //'left-bottom'
28684 // byte, 8-bit unsigned int:
28686 getValue: function (dataView, dataOffset) {
28687 return dataView.getUint8(dataOffset);
28691 // ascii, 8-bit byte:
28693 getValue: function (dataView, dataOffset) {
28694 return String.fromCharCode(dataView.getUint8(dataOffset));
28699 // short, 16 bit int:
28701 getValue: function (dataView, dataOffset, littleEndian) {
28702 return dataView.getUint16(dataOffset, littleEndian);
28706 // long, 32 bit int:
28708 getValue: function (dataView, dataOffset, littleEndian) {
28709 return dataView.getUint32(dataOffset, littleEndian);
28713 // rational = two long values, first is numerator, second is denominator:
28715 getValue: function (dataView, dataOffset, littleEndian) {
28716 return dataView.getUint32(dataOffset, littleEndian) /
28717 dataView.getUint32(dataOffset + 4, littleEndian);
28721 // slong, 32 bit signed int:
28723 getValue: function (dataView, dataOffset, littleEndian) {
28724 return dataView.getInt32(dataOffset, littleEndian);
28728 // srational, two slongs, first is numerator, second is denominator:
28730 getValue: function (dataView, dataOffset, littleEndian) {
28731 return dataView.getInt32(dataOffset, littleEndian) /
28732 dataView.getInt32(dataOffset + 4, littleEndian);
28742 cls : 'btn-group roo-upload-cropbox-rotate-left',
28743 action : 'rotate-left',
28747 cls : 'btn btn-default',
28748 html : '<i class="fa fa-undo"></i>'
28754 cls : 'btn-group roo-upload-cropbox-picture',
28755 action : 'picture',
28759 cls : 'btn btn-default',
28760 html : '<i class="fa fa-picture-o"></i>'
28766 cls : 'btn-group roo-upload-cropbox-rotate-right',
28767 action : 'rotate-right',
28771 cls : 'btn btn-default',
28772 html : '<i class="fa fa-repeat"></i>'
28780 cls : 'btn-group roo-upload-cropbox-rotate-left',
28781 action : 'rotate-left',
28785 cls : 'btn btn-default',
28786 html : '<i class="fa fa-undo"></i>'
28792 cls : 'btn-group roo-upload-cropbox-download',
28793 action : 'download',
28797 cls : 'btn btn-default',
28798 html : '<i class="fa fa-download"></i>'
28804 cls : 'btn-group roo-upload-cropbox-crop',
28809 cls : 'btn btn-default',
28810 html : '<i class="fa fa-crop"></i>'
28816 cls : 'btn-group roo-upload-cropbox-trash',
28821 cls : 'btn btn-default',
28822 html : '<i class="fa fa-trash"></i>'
28828 cls : 'btn-group roo-upload-cropbox-rotate-right',
28829 action : 'rotate-right',
28833 cls : 'btn btn-default',
28834 html : '<i class="fa fa-repeat"></i>'
28842 cls : 'btn-group roo-upload-cropbox-rotate-left',
28843 action : 'rotate-left',
28847 cls : 'btn btn-default',
28848 html : '<i class="fa fa-undo"></i>'
28854 cls : 'btn-group roo-upload-cropbox-rotate-right',
28855 action : 'rotate-right',
28859 cls : 'btn btn-default',
28860 html : '<i class="fa fa-repeat"></i>'
28873 * @class Roo.bootstrap.DocumentManager
28874 * @extends Roo.bootstrap.Component
28875 * Bootstrap DocumentManager class
28876 * @cfg {String} paramName default 'imageUpload'
28877 * @cfg {String} toolTipName default 'filename'
28878 * @cfg {String} method default POST
28879 * @cfg {String} url action url
28880 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28881 * @cfg {Boolean} multiple multiple upload default true
28882 * @cfg {Number} thumbSize default 300
28883 * @cfg {String} fieldLabel
28884 * @cfg {Number} labelWidth default 4
28885 * @cfg {String} labelAlign (left|top) default left
28886 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28887 * @cfg {Number} labellg set the width of label (1-12)
28888 * @cfg {Number} labelmd set the width of label (1-12)
28889 * @cfg {Number} labelsm set the width of label (1-12)
28890 * @cfg {Number} labelxs set the width of label (1-12)
28893 * Create a new DocumentManager
28894 * @param {Object} config The config object
28897 Roo.bootstrap.DocumentManager = function(config){
28898 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28901 this.delegates = [];
28906 * Fire when initial the DocumentManager
28907 * @param {Roo.bootstrap.DocumentManager} this
28912 * inspect selected file
28913 * @param {Roo.bootstrap.DocumentManager} this
28914 * @param {File} file
28919 * Fire when xhr load exception
28920 * @param {Roo.bootstrap.DocumentManager} this
28921 * @param {XMLHttpRequest} xhr
28923 "exception" : true,
28925 * @event afterupload
28926 * Fire when xhr load exception
28927 * @param {Roo.bootstrap.DocumentManager} this
28928 * @param {XMLHttpRequest} xhr
28930 "afterupload" : true,
28933 * prepare the form data
28934 * @param {Roo.bootstrap.DocumentManager} this
28935 * @param {Object} formData
28940 * Fire when remove the file
28941 * @param {Roo.bootstrap.DocumentManager} this
28942 * @param {Object} file
28947 * Fire after refresh the file
28948 * @param {Roo.bootstrap.DocumentManager} this
28953 * Fire after click the image
28954 * @param {Roo.bootstrap.DocumentManager} this
28955 * @param {Object} file
28960 * Fire when upload a image and editable set to true
28961 * @param {Roo.bootstrap.DocumentManager} this
28962 * @param {Object} file
28966 * @event beforeselectfile
28967 * Fire before select file
28968 * @param {Roo.bootstrap.DocumentManager} this
28970 "beforeselectfile" : true,
28973 * Fire before process file
28974 * @param {Roo.bootstrap.DocumentManager} this
28975 * @param {Object} file
28979 * @event previewrendered
28980 * Fire when preview rendered
28981 * @param {Roo.bootstrap.DocumentManager} this
28982 * @param {Object} file
28984 "previewrendered" : true,
28987 "previewResize" : true
28992 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29001 paramName : 'imageUpload',
29002 toolTipName : 'filename',
29005 labelAlign : 'left',
29015 getAutoCreate : function()
29017 var managerWidget = {
29019 cls : 'roo-document-manager',
29023 cls : 'roo-document-manager-selector',
29028 cls : 'roo-document-manager-uploader',
29032 cls : 'roo-document-manager-upload-btn',
29033 html : '<i class="fa fa-plus"></i>'
29044 cls : 'column col-md-12',
29049 if(this.fieldLabel.length){
29054 cls : 'column col-md-12',
29055 html : this.fieldLabel
29059 cls : 'column col-md-12',
29064 if(this.labelAlign == 'left'){
29069 html : this.fieldLabel
29078 if(this.labelWidth > 12){
29079 content[0].style = "width: " + this.labelWidth + 'px';
29082 if(this.labelWidth < 13 && this.labelmd == 0){
29083 this.labelmd = this.labelWidth;
29086 if(this.labellg > 0){
29087 content[0].cls += ' col-lg-' + this.labellg;
29088 content[1].cls += ' col-lg-' + (12 - this.labellg);
29091 if(this.labelmd > 0){
29092 content[0].cls += ' col-md-' + this.labelmd;
29093 content[1].cls += ' col-md-' + (12 - this.labelmd);
29096 if(this.labelsm > 0){
29097 content[0].cls += ' col-sm-' + this.labelsm;
29098 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29101 if(this.labelxs > 0){
29102 content[0].cls += ' col-xs-' + this.labelxs;
29103 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29111 cls : 'row clearfix',
29119 initEvents : function()
29121 this.managerEl = this.el.select('.roo-document-manager', true).first();
29122 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29124 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29125 this.selectorEl.hide();
29128 this.selectorEl.attr('multiple', 'multiple');
29131 this.selectorEl.on('change', this.onFileSelected, this);
29133 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29134 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29136 this.uploader.on('click', this.onUploaderClick, this);
29138 this.renderProgressDialog();
29142 window.addEventListener("resize", function() { _this.refresh(); } );
29144 this.fireEvent('initial', this);
29147 renderProgressDialog : function()
29151 this.progressDialog = new Roo.bootstrap.Modal({
29152 cls : 'roo-document-manager-progress-dialog',
29153 allow_close : false,
29163 btnclick : function() {
29164 _this.uploadCancel();
29170 this.progressDialog.render(Roo.get(document.body));
29172 this.progress = new Roo.bootstrap.Progress({
29173 cls : 'roo-document-manager-progress',
29178 this.progress.render(this.progressDialog.getChildContainer());
29180 this.progressBar = new Roo.bootstrap.ProgressBar({
29181 cls : 'roo-document-manager-progress-bar',
29184 aria_valuemax : 12,
29188 this.progressBar.render(this.progress.getChildContainer());
29191 onUploaderClick : function(e)
29193 e.preventDefault();
29195 if(this.fireEvent('beforeselectfile', this) != false){
29196 this.selectorEl.dom.click();
29201 onFileSelected : function(e)
29203 e.preventDefault();
29205 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29209 Roo.each(this.selectorEl.dom.files, function(file){
29210 if(this.fireEvent('inspect', this, file) != false){
29211 this.files.push(file);
29221 this.selectorEl.dom.value = '';
29223 if(!this.files || !this.files.length){
29227 if(this.boxes > 0 && this.files.length > this.boxes){
29228 this.files = this.files.slice(0, this.boxes);
29231 this.uploader.show();
29233 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29234 this.uploader.hide();
29243 Roo.each(this.files, function(file){
29245 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29246 var f = this.renderPreview(file);
29251 if(file.type.indexOf('image') != -1){
29252 this.delegates.push(
29254 _this.process(file);
29255 }).createDelegate(this)
29263 _this.process(file);
29264 }).createDelegate(this)
29269 this.files = files;
29271 this.delegates = this.delegates.concat(docs);
29273 if(!this.delegates.length){
29278 this.progressBar.aria_valuemax = this.delegates.length;
29285 arrange : function()
29287 if(!this.delegates.length){
29288 this.progressDialog.hide();
29293 var delegate = this.delegates.shift();
29295 this.progressDialog.show();
29297 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29299 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29304 refresh : function()
29306 this.uploader.show();
29308 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29309 this.uploader.hide();
29312 Roo.isTouch ? this.closable(false) : this.closable(true);
29314 this.fireEvent('refresh', this);
29317 onRemove : function(e, el, o)
29319 e.preventDefault();
29321 this.fireEvent('remove', this, o);
29325 remove : function(o)
29329 Roo.each(this.files, function(file){
29330 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29339 this.files = files;
29346 Roo.each(this.files, function(file){
29351 file.target.remove();
29360 onClick : function(e, el, o)
29362 e.preventDefault();
29364 this.fireEvent('click', this, o);
29368 closable : function(closable)
29370 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29372 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29384 xhrOnLoad : function(xhr)
29386 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29390 if (xhr.readyState !== 4) {
29392 this.fireEvent('exception', this, xhr);
29396 var response = Roo.decode(xhr.responseText);
29398 if(!response.success){
29400 this.fireEvent('exception', this, xhr);
29404 var file = this.renderPreview(response.data);
29406 this.files.push(file);
29410 this.fireEvent('afterupload', this, xhr);
29414 xhrOnError : function(xhr)
29416 Roo.log('xhr on error');
29418 var response = Roo.decode(xhr.responseText);
29425 process : function(file)
29427 if(this.fireEvent('process', this, file) !== false){
29428 if(this.editable && file.type.indexOf('image') != -1){
29429 this.fireEvent('edit', this, file);
29433 this.uploadStart(file, false);
29440 uploadStart : function(file, crop)
29442 this.xhr = new XMLHttpRequest();
29444 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29449 file.xhr = this.xhr;
29451 this.managerEl.createChild({
29453 cls : 'roo-document-manager-loading',
29457 tooltip : file.name,
29458 cls : 'roo-document-manager-thumb',
29459 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29465 this.xhr.open(this.method, this.url, true);
29468 "Accept": "application/json",
29469 "Cache-Control": "no-cache",
29470 "X-Requested-With": "XMLHttpRequest"
29473 for (var headerName in headers) {
29474 var headerValue = headers[headerName];
29476 this.xhr.setRequestHeader(headerName, headerValue);
29482 this.xhr.onload = function()
29484 _this.xhrOnLoad(_this.xhr);
29487 this.xhr.onerror = function()
29489 _this.xhrOnError(_this.xhr);
29492 var formData = new FormData();
29494 formData.append('returnHTML', 'NO');
29497 formData.append('crop', crop);
29500 formData.append(this.paramName, file, file.name);
29507 if(this.fireEvent('prepare', this, formData, options) != false){
29509 if(options.manually){
29513 this.xhr.send(formData);
29517 this.uploadCancel();
29520 uploadCancel : function()
29526 this.delegates = [];
29528 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29535 renderPreview : function(file)
29537 if(typeof(file.target) != 'undefined' && file.target){
29541 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29543 var previewEl = this.managerEl.createChild({
29545 cls : 'roo-document-manager-preview',
29549 tooltip : file[this.toolTipName],
29550 cls : 'roo-document-manager-thumb',
29551 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29556 html : '<i class="fa fa-times-circle"></i>'
29561 var close = previewEl.select('button.close', true).first();
29563 close.on('click', this.onRemove, this, file);
29565 file.target = previewEl;
29567 var image = previewEl.select('img', true).first();
29571 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29573 image.on('click', this.onClick, this, file);
29575 this.fireEvent('previewrendered', this, file);
29581 onPreviewLoad : function(file, image)
29583 if(typeof(file.target) == 'undefined' || !file.target){
29587 var width = image.dom.naturalWidth || image.dom.width;
29588 var height = image.dom.naturalHeight || image.dom.height;
29590 if(!this.previewResize) {
29594 if(width > height){
29595 file.target.addClass('wide');
29599 file.target.addClass('tall');
29604 uploadFromSource : function(file, crop)
29606 this.xhr = new XMLHttpRequest();
29608 this.managerEl.createChild({
29610 cls : 'roo-document-manager-loading',
29614 tooltip : file.name,
29615 cls : 'roo-document-manager-thumb',
29616 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29622 this.xhr.open(this.method, this.url, true);
29625 "Accept": "application/json",
29626 "Cache-Control": "no-cache",
29627 "X-Requested-With": "XMLHttpRequest"
29630 for (var headerName in headers) {
29631 var headerValue = headers[headerName];
29633 this.xhr.setRequestHeader(headerName, headerValue);
29639 this.xhr.onload = function()
29641 _this.xhrOnLoad(_this.xhr);
29644 this.xhr.onerror = function()
29646 _this.xhrOnError(_this.xhr);
29649 var formData = new FormData();
29651 formData.append('returnHTML', 'NO');
29653 formData.append('crop', crop);
29655 if(typeof(file.filename) != 'undefined'){
29656 formData.append('filename', file.filename);
29659 if(typeof(file.mimetype) != 'undefined'){
29660 formData.append('mimetype', file.mimetype);
29665 if(this.fireEvent('prepare', this, formData) != false){
29666 this.xhr.send(formData);
29676 * @class Roo.bootstrap.DocumentViewer
29677 * @extends Roo.bootstrap.Component
29678 * Bootstrap DocumentViewer class
29679 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29680 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29683 * Create a new DocumentViewer
29684 * @param {Object} config The config object
29687 Roo.bootstrap.DocumentViewer = function(config){
29688 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29693 * Fire after initEvent
29694 * @param {Roo.bootstrap.DocumentViewer} this
29700 * @param {Roo.bootstrap.DocumentViewer} this
29705 * Fire after download button
29706 * @param {Roo.bootstrap.DocumentViewer} this
29711 * Fire after trash button
29712 * @param {Roo.bootstrap.DocumentViewer} this
29719 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29721 showDownload : true,
29725 getAutoCreate : function()
29729 cls : 'roo-document-viewer',
29733 cls : 'roo-document-viewer-body',
29737 cls : 'roo-document-viewer-thumb',
29741 cls : 'roo-document-viewer-image'
29749 cls : 'roo-document-viewer-footer',
29752 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29756 cls : 'btn-group roo-document-viewer-download',
29760 cls : 'btn btn-default',
29761 html : '<i class="fa fa-download"></i>'
29767 cls : 'btn-group roo-document-viewer-trash',
29771 cls : 'btn btn-default',
29772 html : '<i class="fa fa-trash"></i>'
29785 initEvents : function()
29787 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29788 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29790 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29791 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29793 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29794 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29796 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29797 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29799 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29800 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29802 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29803 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29805 this.bodyEl.on('click', this.onClick, this);
29806 this.downloadBtn.on('click', this.onDownload, this);
29807 this.trashBtn.on('click', this.onTrash, this);
29809 this.downloadBtn.hide();
29810 this.trashBtn.hide();
29812 if(this.showDownload){
29813 this.downloadBtn.show();
29816 if(this.showTrash){
29817 this.trashBtn.show();
29820 if(!this.showDownload && !this.showTrash) {
29821 this.footerEl.hide();
29826 initial : function()
29828 this.fireEvent('initial', this);
29832 onClick : function(e)
29834 e.preventDefault();
29836 this.fireEvent('click', this);
29839 onDownload : function(e)
29841 e.preventDefault();
29843 this.fireEvent('download', this);
29846 onTrash : function(e)
29848 e.preventDefault();
29850 this.fireEvent('trash', this);
29862 * @class Roo.bootstrap.NavProgressBar
29863 * @extends Roo.bootstrap.Component
29864 * Bootstrap NavProgressBar class
29867 * Create a new nav progress bar
29868 * @param {Object} config The config object
29871 Roo.bootstrap.NavProgressBar = function(config){
29872 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29874 this.bullets = this.bullets || [];
29876 // Roo.bootstrap.NavProgressBar.register(this);
29880 * Fires when the active item changes
29881 * @param {Roo.bootstrap.NavProgressBar} this
29882 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29883 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29890 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29895 getAutoCreate : function()
29897 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29901 cls : 'roo-navigation-bar-group',
29905 cls : 'roo-navigation-top-bar'
29909 cls : 'roo-navigation-bullets-bar',
29913 cls : 'roo-navigation-bar'
29920 cls : 'roo-navigation-bottom-bar'
29930 initEvents: function()
29935 onRender : function(ct, position)
29937 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29939 if(this.bullets.length){
29940 Roo.each(this.bullets, function(b){
29949 addItem : function(cfg)
29951 var item = new Roo.bootstrap.NavProgressItem(cfg);
29953 item.parentId = this.id;
29954 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29957 var top = new Roo.bootstrap.Element({
29959 cls : 'roo-navigation-bar-text'
29962 var bottom = new Roo.bootstrap.Element({
29964 cls : 'roo-navigation-bar-text'
29967 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29968 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29970 var topText = new Roo.bootstrap.Element({
29972 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29975 var bottomText = new Roo.bootstrap.Element({
29977 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29980 topText.onRender(top.el, null);
29981 bottomText.onRender(bottom.el, null);
29984 item.bottomEl = bottom;
29987 this.barItems.push(item);
29992 getActive : function()
29994 var active = false;
29996 Roo.each(this.barItems, function(v){
29998 if (!v.isActive()) {
30010 setActiveItem : function(item)
30014 Roo.each(this.barItems, function(v){
30015 if (v.rid == item.rid) {
30019 if (v.isActive()) {
30020 v.setActive(false);
30025 item.setActive(true);
30027 this.fireEvent('changed', this, item, prev);
30030 getBarItem: function(rid)
30034 Roo.each(this.barItems, function(e) {
30035 if (e.rid != rid) {
30046 indexOfItem : function(item)
30050 Roo.each(this.barItems, function(v, i){
30052 if (v.rid != item.rid) {
30063 setActiveNext : function()
30065 var i = this.indexOfItem(this.getActive());
30067 if (i > this.barItems.length) {
30071 this.setActiveItem(this.barItems[i+1]);
30074 setActivePrev : function()
30076 var i = this.indexOfItem(this.getActive());
30082 this.setActiveItem(this.barItems[i-1]);
30085 format : function()
30087 if(!this.barItems.length){
30091 var width = 100 / this.barItems.length;
30093 Roo.each(this.barItems, function(i){
30094 i.el.setStyle('width', width + '%');
30095 i.topEl.el.setStyle('width', width + '%');
30096 i.bottomEl.el.setStyle('width', width + '%');
30105 * Nav Progress Item
30110 * @class Roo.bootstrap.NavProgressItem
30111 * @extends Roo.bootstrap.Component
30112 * Bootstrap NavProgressItem class
30113 * @cfg {String} rid the reference id
30114 * @cfg {Boolean} active (true|false) Is item active default false
30115 * @cfg {Boolean} disabled (true|false) Is item active default false
30116 * @cfg {String} html
30117 * @cfg {String} position (top|bottom) text position default bottom
30118 * @cfg {String} icon show icon instead of number
30121 * Create a new NavProgressItem
30122 * @param {Object} config The config object
30124 Roo.bootstrap.NavProgressItem = function(config){
30125 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30130 * The raw click event for the entire grid.
30131 * @param {Roo.bootstrap.NavProgressItem} this
30132 * @param {Roo.EventObject} e
30139 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30145 position : 'bottom',
30148 getAutoCreate : function()
30150 var iconCls = 'roo-navigation-bar-item-icon';
30152 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30156 cls: 'roo-navigation-bar-item',
30166 cfg.cls += ' active';
30169 cfg.cls += ' disabled';
30175 disable : function()
30177 this.setDisabled(true);
30180 enable : function()
30182 this.setDisabled(false);
30185 initEvents: function()
30187 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30189 this.iconEl.on('click', this.onClick, this);
30192 onClick : function(e)
30194 e.preventDefault();
30200 if(this.fireEvent('click', this, e) === false){
30204 this.parent().setActiveItem(this);
30207 isActive: function ()
30209 return this.active;
30212 setActive : function(state)
30214 if(this.active == state){
30218 this.active = state;
30221 this.el.addClass('active');
30225 this.el.removeClass('active');
30230 setDisabled : function(state)
30232 if(this.disabled == state){
30236 this.disabled = state;
30239 this.el.addClass('disabled');
30243 this.el.removeClass('disabled');
30246 tooltipEl : function()
30248 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30261 * @class Roo.bootstrap.FieldLabel
30262 * @extends Roo.bootstrap.Component
30263 * Bootstrap FieldLabel class
30264 * @cfg {String} html contents of the element
30265 * @cfg {String} tag tag of the element default label
30266 * @cfg {String} cls class of the element
30267 * @cfg {String} target label target
30268 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30269 * @cfg {String} invalidClass default "text-warning"
30270 * @cfg {String} validClass default "text-success"
30271 * @cfg {String} iconTooltip default "This field is required"
30272 * @cfg {String} indicatorpos (left|right) default left
30275 * Create a new FieldLabel
30276 * @param {Object} config The config object
30279 Roo.bootstrap.FieldLabel = function(config){
30280 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30285 * Fires after the field has been marked as invalid.
30286 * @param {Roo.form.FieldLabel} this
30287 * @param {String} msg The validation message
30292 * Fires after the field has been validated with no errors.
30293 * @param {Roo.form.FieldLabel} this
30299 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30306 invalidClass : 'has-warning',
30307 validClass : 'has-success',
30308 iconTooltip : 'This field is required',
30309 indicatorpos : 'left',
30311 getAutoCreate : function(){
30314 if (!this.allowBlank) {
30320 cls : 'roo-bootstrap-field-label ' + this.cls,
30325 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30326 tooltip : this.iconTooltip
30335 if(this.indicatorpos == 'right'){
30338 cls : 'roo-bootstrap-field-label ' + this.cls,
30347 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30348 tooltip : this.iconTooltip
30357 initEvents: function()
30359 Roo.bootstrap.Element.superclass.initEvents.call(this);
30361 this.indicator = this.indicatorEl();
30363 if(this.indicator){
30364 this.indicator.removeClass('visible');
30365 this.indicator.addClass('invisible');
30368 Roo.bootstrap.FieldLabel.register(this);
30371 indicatorEl : function()
30373 var indicator = this.el.select('i.roo-required-indicator',true).first();
30384 * Mark this field as valid
30386 markValid : function()
30388 if(this.indicator){
30389 this.indicator.removeClass('visible');
30390 this.indicator.addClass('invisible');
30393 this.el.removeClass(this.invalidClass);
30395 this.el.addClass(this.validClass);
30397 this.fireEvent('valid', this);
30401 * Mark this field as invalid
30402 * @param {String} msg The validation message
30404 markInvalid : function(msg)
30406 if(this.indicator){
30407 this.indicator.removeClass('invisible');
30408 this.indicator.addClass('visible');
30411 this.el.removeClass(this.validClass);
30413 this.el.addClass(this.invalidClass);
30415 this.fireEvent('invalid', this, msg);
30421 Roo.apply(Roo.bootstrap.FieldLabel, {
30426 * register a FieldLabel Group
30427 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30429 register : function(label)
30431 if(this.groups.hasOwnProperty(label.target)){
30435 this.groups[label.target] = label;
30439 * fetch a FieldLabel Group based on the target
30440 * @param {string} target
30441 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30443 get: function(target) {
30444 if (typeof(this.groups[target]) == 'undefined') {
30448 return this.groups[target] ;
30457 * page DateSplitField.
30463 * @class Roo.bootstrap.DateSplitField
30464 * @extends Roo.bootstrap.Component
30465 * Bootstrap DateSplitField class
30466 * @cfg {string} fieldLabel - the label associated
30467 * @cfg {Number} labelWidth set the width of label (0-12)
30468 * @cfg {String} labelAlign (top|left)
30469 * @cfg {Boolean} dayAllowBlank (true|false) default false
30470 * @cfg {Boolean} monthAllowBlank (true|false) default false
30471 * @cfg {Boolean} yearAllowBlank (true|false) default false
30472 * @cfg {string} dayPlaceholder
30473 * @cfg {string} monthPlaceholder
30474 * @cfg {string} yearPlaceholder
30475 * @cfg {string} dayFormat default 'd'
30476 * @cfg {string} monthFormat default 'm'
30477 * @cfg {string} yearFormat default 'Y'
30478 * @cfg {Number} labellg set the width of label (1-12)
30479 * @cfg {Number} labelmd set the width of label (1-12)
30480 * @cfg {Number} labelsm set the width of label (1-12)
30481 * @cfg {Number} labelxs set the width of label (1-12)
30485 * Create a new DateSplitField
30486 * @param {Object} config The config object
30489 Roo.bootstrap.DateSplitField = function(config){
30490 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30496 * getting the data of years
30497 * @param {Roo.bootstrap.DateSplitField} this
30498 * @param {Object} years
30503 * getting the data of days
30504 * @param {Roo.bootstrap.DateSplitField} this
30505 * @param {Object} days
30510 * Fires after the field has been marked as invalid.
30511 * @param {Roo.form.Field} this
30512 * @param {String} msg The validation message
30517 * Fires after the field has been validated with no errors.
30518 * @param {Roo.form.Field} this
30524 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30527 labelAlign : 'top',
30529 dayAllowBlank : false,
30530 monthAllowBlank : false,
30531 yearAllowBlank : false,
30532 dayPlaceholder : '',
30533 monthPlaceholder : '',
30534 yearPlaceholder : '',
30538 isFormField : true,
30544 getAutoCreate : function()
30548 cls : 'row roo-date-split-field-group',
30553 cls : 'form-hidden-field roo-date-split-field-group-value',
30559 var labelCls = 'col-md-12';
30560 var contentCls = 'col-md-4';
30562 if(this.fieldLabel){
30566 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30570 html : this.fieldLabel
30575 if(this.labelAlign == 'left'){
30577 if(this.labelWidth > 12){
30578 label.style = "width: " + this.labelWidth + 'px';
30581 if(this.labelWidth < 13 && this.labelmd == 0){
30582 this.labelmd = this.labelWidth;
30585 if(this.labellg > 0){
30586 labelCls = ' col-lg-' + this.labellg;
30587 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30590 if(this.labelmd > 0){
30591 labelCls = ' col-md-' + this.labelmd;
30592 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30595 if(this.labelsm > 0){
30596 labelCls = ' col-sm-' + this.labelsm;
30597 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30600 if(this.labelxs > 0){
30601 labelCls = ' col-xs-' + this.labelxs;
30602 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30606 label.cls += ' ' + labelCls;
30608 cfg.cn.push(label);
30611 Roo.each(['day', 'month', 'year'], function(t){
30614 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30621 inputEl: function ()
30623 return this.el.select('.roo-date-split-field-group-value', true).first();
30626 onRender : function(ct, position)
30630 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30632 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30634 this.dayField = new Roo.bootstrap.ComboBox({
30635 allowBlank : this.dayAllowBlank,
30636 alwaysQuery : true,
30637 displayField : 'value',
30640 forceSelection : true,
30642 placeholder : this.dayPlaceholder,
30643 selectOnFocus : true,
30644 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30645 triggerAction : 'all',
30647 valueField : 'value',
30648 store : new Roo.data.SimpleStore({
30649 data : (function() {
30651 _this.fireEvent('days', _this, days);
30654 fields : [ 'value' ]
30657 select : function (_self, record, index)
30659 _this.setValue(_this.getValue());
30664 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30666 this.monthField = new Roo.bootstrap.MonthField({
30667 after : '<i class=\"fa fa-calendar\"></i>',
30668 allowBlank : this.monthAllowBlank,
30669 placeholder : this.monthPlaceholder,
30672 render : function (_self)
30674 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30675 e.preventDefault();
30679 select : function (_self, oldvalue, newvalue)
30681 _this.setValue(_this.getValue());
30686 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30688 this.yearField = new Roo.bootstrap.ComboBox({
30689 allowBlank : this.yearAllowBlank,
30690 alwaysQuery : true,
30691 displayField : 'value',
30694 forceSelection : true,
30696 placeholder : this.yearPlaceholder,
30697 selectOnFocus : true,
30698 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30699 triggerAction : 'all',
30701 valueField : 'value',
30702 store : new Roo.data.SimpleStore({
30703 data : (function() {
30705 _this.fireEvent('years', _this, years);
30708 fields : [ 'value' ]
30711 select : function (_self, record, index)
30713 _this.setValue(_this.getValue());
30718 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30721 setValue : function(v, format)
30723 this.inputEl.dom.value = v;
30725 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30727 var d = Date.parseDate(v, f);
30734 this.setDay(d.format(this.dayFormat));
30735 this.setMonth(d.format(this.monthFormat));
30736 this.setYear(d.format(this.yearFormat));
30743 setDay : function(v)
30745 this.dayField.setValue(v);
30746 this.inputEl.dom.value = this.getValue();
30751 setMonth : function(v)
30753 this.monthField.setValue(v, true);
30754 this.inputEl.dom.value = this.getValue();
30759 setYear : function(v)
30761 this.yearField.setValue(v);
30762 this.inputEl.dom.value = this.getValue();
30767 getDay : function()
30769 return this.dayField.getValue();
30772 getMonth : function()
30774 return this.monthField.getValue();
30777 getYear : function()
30779 return this.yearField.getValue();
30782 getValue : function()
30784 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30786 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30796 this.inputEl.dom.value = '';
30801 validate : function()
30803 var d = this.dayField.validate();
30804 var m = this.monthField.validate();
30805 var y = this.yearField.validate();
30810 (!this.dayAllowBlank && !d) ||
30811 (!this.monthAllowBlank && !m) ||
30812 (!this.yearAllowBlank && !y)
30817 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30826 this.markInvalid();
30831 markValid : function()
30834 var label = this.el.select('label', true).first();
30835 var icon = this.el.select('i.fa-star', true).first();
30841 this.fireEvent('valid', this);
30845 * Mark this field as invalid
30846 * @param {String} msg The validation message
30848 markInvalid : function(msg)
30851 var label = this.el.select('label', true).first();
30852 var icon = this.el.select('i.fa-star', true).first();
30854 if(label && !icon){
30855 this.el.select('.roo-date-split-field-label', true).createChild({
30857 cls : 'text-danger fa fa-lg fa-star',
30858 tooltip : 'This field is required',
30859 style : 'margin-right:5px;'
30863 this.fireEvent('invalid', this, msg);
30866 clearInvalid : function()
30868 var label = this.el.select('label', true).first();
30869 var icon = this.el.select('i.fa-star', true).first();
30875 this.fireEvent('valid', this);
30878 getName: function()
30888 * http://masonry.desandro.com
30890 * The idea is to render all the bricks based on vertical width...
30892 * The original code extends 'outlayer' - we might need to use that....
30898 * @class Roo.bootstrap.LayoutMasonry
30899 * @extends Roo.bootstrap.Component
30900 * Bootstrap Layout Masonry class
30903 * Create a new Element
30904 * @param {Object} config The config object
30907 Roo.bootstrap.LayoutMasonry = function(config){
30909 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30913 Roo.bootstrap.LayoutMasonry.register(this);
30919 * Fire after layout the items
30920 * @param {Roo.bootstrap.LayoutMasonry} this
30921 * @param {Roo.EventObject} e
30928 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30931 * @cfg {Boolean} isLayoutInstant = no animation?
30933 isLayoutInstant : false, // needed?
30936 * @cfg {Number} boxWidth width of the columns
30941 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30946 * @cfg {Number} padWidth padding below box..
30951 * @cfg {Number} gutter gutter width..
30956 * @cfg {Number} maxCols maximum number of columns
30962 * @cfg {Boolean} isAutoInitial defalut true
30964 isAutoInitial : true,
30969 * @cfg {Boolean} isHorizontal defalut false
30971 isHorizontal : false,
30973 currentSize : null,
30979 bricks: null, //CompositeElement
30983 _isLayoutInited : false,
30985 // isAlternative : false, // only use for vertical layout...
30988 * @cfg {Number} alternativePadWidth padding below box..
30990 alternativePadWidth : 50,
30992 selectedBrick : [],
30994 getAutoCreate : function(){
30996 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31000 cls: 'blog-masonary-wrapper ' + this.cls,
31002 cls : 'mas-boxes masonary'
31009 getChildContainer: function( )
31011 if (this.boxesEl) {
31012 return this.boxesEl;
31015 this.boxesEl = this.el.select('.mas-boxes').first();
31017 return this.boxesEl;
31021 initEvents : function()
31025 if(this.isAutoInitial){
31026 Roo.log('hook children rendered');
31027 this.on('childrenrendered', function() {
31028 Roo.log('children rendered');
31034 initial : function()
31036 this.selectedBrick = [];
31038 this.currentSize = this.el.getBox(true);
31040 Roo.EventManager.onWindowResize(this.resize, this);
31042 if(!this.isAutoInitial){
31050 //this.layout.defer(500,this);
31054 resize : function()
31056 var cs = this.el.getBox(true);
31059 this.currentSize.width == cs.width &&
31060 this.currentSize.x == cs.x &&
31061 this.currentSize.height == cs.height &&
31062 this.currentSize.y == cs.y
31064 Roo.log("no change in with or X or Y");
31068 this.currentSize = cs;
31074 layout : function()
31076 this._resetLayout();
31078 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31080 this.layoutItems( isInstant );
31082 this._isLayoutInited = true;
31084 this.fireEvent('layout', this);
31088 _resetLayout : function()
31090 if(this.isHorizontal){
31091 this.horizontalMeasureColumns();
31095 this.verticalMeasureColumns();
31099 verticalMeasureColumns : function()
31101 this.getContainerWidth();
31103 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31104 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31108 var boxWidth = this.boxWidth + this.padWidth;
31110 if(this.containerWidth < this.boxWidth){
31111 boxWidth = this.containerWidth
31114 var containerWidth = this.containerWidth;
31116 var cols = Math.floor(containerWidth / boxWidth);
31118 this.cols = Math.max( cols, 1 );
31120 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31122 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31124 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31126 this.colWidth = boxWidth + avail - this.padWidth;
31128 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31129 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31132 horizontalMeasureColumns : function()
31134 this.getContainerWidth();
31136 var boxWidth = this.boxWidth;
31138 if(this.containerWidth < boxWidth){
31139 boxWidth = this.containerWidth;
31142 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31144 this.el.setHeight(boxWidth);
31148 getContainerWidth : function()
31150 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31153 layoutItems : function( isInstant )
31155 Roo.log(this.bricks);
31157 var items = Roo.apply([], this.bricks);
31159 if(this.isHorizontal){
31160 this._horizontalLayoutItems( items , isInstant );
31164 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31165 // this._verticalAlternativeLayoutItems( items , isInstant );
31169 this._verticalLayoutItems( items , isInstant );
31173 _verticalLayoutItems : function ( items , isInstant)
31175 if ( !items || !items.length ) {
31180 ['xs', 'xs', 'xs', 'tall'],
31181 ['xs', 'xs', 'tall'],
31182 ['xs', 'xs', 'sm'],
31183 ['xs', 'xs', 'xs'],
31189 ['sm', 'xs', 'xs'],
31193 ['tall', 'xs', 'xs', 'xs'],
31194 ['tall', 'xs', 'xs'],
31206 Roo.each(items, function(item, k){
31208 switch (item.size) {
31209 // these layouts take up a full box,
31220 boxes.push([item]);
31243 var filterPattern = function(box, length)
31251 var pattern = box.slice(0, length);
31255 Roo.each(pattern, function(i){
31256 format.push(i.size);
31259 Roo.each(standard, function(s){
31261 if(String(s) != String(format)){
31270 if(!match && length == 1){
31275 filterPattern(box, length - 1);
31279 queue.push(pattern);
31281 box = box.slice(length, box.length);
31283 filterPattern(box, 4);
31289 Roo.each(boxes, function(box, k){
31295 if(box.length == 1){
31300 filterPattern(box, 4);
31304 this._processVerticalLayoutQueue( queue, isInstant );
31308 // _verticalAlternativeLayoutItems : function( items , isInstant )
31310 // if ( !items || !items.length ) {
31314 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31318 _horizontalLayoutItems : function ( items , isInstant)
31320 if ( !items || !items.length || items.length < 3) {
31326 var eItems = items.slice(0, 3);
31328 items = items.slice(3, items.length);
31331 ['xs', 'xs', 'xs', 'wide'],
31332 ['xs', 'xs', 'wide'],
31333 ['xs', 'xs', 'sm'],
31334 ['xs', 'xs', 'xs'],
31340 ['sm', 'xs', 'xs'],
31344 ['wide', 'xs', 'xs', 'xs'],
31345 ['wide', 'xs', 'xs'],
31358 Roo.each(items, function(item, k){
31360 switch (item.size) {
31371 boxes.push([item]);
31395 var filterPattern = function(box, length)
31403 var pattern = box.slice(0, length);
31407 Roo.each(pattern, function(i){
31408 format.push(i.size);
31411 Roo.each(standard, function(s){
31413 if(String(s) != String(format)){
31422 if(!match && length == 1){
31427 filterPattern(box, length - 1);
31431 queue.push(pattern);
31433 box = box.slice(length, box.length);
31435 filterPattern(box, 4);
31441 Roo.each(boxes, function(box, k){
31447 if(box.length == 1){
31452 filterPattern(box, 4);
31459 var pos = this.el.getBox(true);
31463 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31465 var hit_end = false;
31467 Roo.each(queue, function(box){
31471 Roo.each(box, function(b){
31473 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31483 Roo.each(box, function(b){
31485 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31488 mx = Math.max(mx, b.x);
31492 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31496 Roo.each(box, function(b){
31498 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31512 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31515 /** Sets position of item in DOM
31516 * @param {Element} item
31517 * @param {Number} x - horizontal position
31518 * @param {Number} y - vertical position
31519 * @param {Boolean} isInstant - disables transitions
31521 _processVerticalLayoutQueue : function( queue, isInstant )
31523 var pos = this.el.getBox(true);
31528 for (var i = 0; i < this.cols; i++){
31532 Roo.each(queue, function(box, k){
31534 var col = k % this.cols;
31536 Roo.each(box, function(b,kk){
31538 b.el.position('absolute');
31540 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31541 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31543 if(b.size == 'md-left' || b.size == 'md-right'){
31544 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31545 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31548 b.el.setWidth(width);
31549 b.el.setHeight(height);
31551 b.el.select('iframe',true).setSize(width,height);
31555 for (var i = 0; i < this.cols; i++){
31557 if(maxY[i] < maxY[col]){
31562 col = Math.min(col, i);
31566 x = pos.x + col * (this.colWidth + this.padWidth);
31570 var positions = [];
31572 switch (box.length){
31574 positions = this.getVerticalOneBoxColPositions(x, y, box);
31577 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31580 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31583 positions = this.getVerticalFourBoxColPositions(x, y, box);
31589 Roo.each(box, function(b,kk){
31591 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31593 var sz = b.el.getSize();
31595 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31603 for (var i = 0; i < this.cols; i++){
31604 mY = Math.max(mY, maxY[i]);
31607 this.el.setHeight(mY - pos.y);
31611 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31613 // var pos = this.el.getBox(true);
31616 // var maxX = pos.right;
31618 // var maxHeight = 0;
31620 // Roo.each(items, function(item, k){
31624 // item.el.position('absolute');
31626 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31628 // item.el.setWidth(width);
31630 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31632 // item.el.setHeight(height);
31635 // item.el.setXY([x, y], isInstant ? false : true);
31637 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31640 // y = y + height + this.alternativePadWidth;
31642 // maxHeight = maxHeight + height + this.alternativePadWidth;
31646 // this.el.setHeight(maxHeight);
31650 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31652 var pos = this.el.getBox(true);
31657 var maxX = pos.right;
31659 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31661 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31663 Roo.each(queue, function(box, k){
31665 Roo.each(box, function(b, kk){
31667 b.el.position('absolute');
31669 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31670 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31672 if(b.size == 'md-left' || b.size == 'md-right'){
31673 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31674 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31677 b.el.setWidth(width);
31678 b.el.setHeight(height);
31686 var positions = [];
31688 switch (box.length){
31690 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31693 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31696 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31699 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31705 Roo.each(box, function(b,kk){
31707 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31709 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31717 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31719 Roo.each(eItems, function(b,k){
31721 b.size = (k == 0) ? 'sm' : 'xs';
31722 b.x = (k == 0) ? 2 : 1;
31723 b.y = (k == 0) ? 2 : 1;
31725 b.el.position('absolute');
31727 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31729 b.el.setWidth(width);
31731 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31733 b.el.setHeight(height);
31737 var positions = [];
31740 x : maxX - this.unitWidth * 2 - this.gutter,
31745 x : maxX - this.unitWidth,
31746 y : minY + (this.unitWidth + this.gutter) * 2
31750 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31754 Roo.each(eItems, function(b,k){
31756 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31762 getVerticalOneBoxColPositions : function(x, y, box)
31766 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31768 if(box[0].size == 'md-left'){
31772 if(box[0].size == 'md-right'){
31777 x : x + (this.unitWidth + this.gutter) * rand,
31784 getVerticalTwoBoxColPositions : function(x, y, box)
31788 if(box[0].size == 'xs'){
31792 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31796 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31810 x : x + (this.unitWidth + this.gutter) * 2,
31811 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31818 getVerticalThreeBoxColPositions : function(x, y, box)
31822 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31830 x : x + (this.unitWidth + this.gutter) * 1,
31835 x : x + (this.unitWidth + this.gutter) * 2,
31843 if(box[0].size == 'xs' && box[1].size == 'xs'){
31852 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31856 x : x + (this.unitWidth + this.gutter) * 1,
31870 x : x + (this.unitWidth + this.gutter) * 2,
31875 x : x + (this.unitWidth + this.gutter) * 2,
31876 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31883 getVerticalFourBoxColPositions : function(x, y, box)
31887 if(box[0].size == 'xs'){
31896 y : y + (this.unitHeight + this.gutter) * 1
31901 y : y + (this.unitHeight + this.gutter) * 2
31905 x : x + (this.unitWidth + this.gutter) * 1,
31919 x : x + (this.unitWidth + this.gutter) * 2,
31924 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31925 y : y + (this.unitHeight + this.gutter) * 1
31929 x : x + (this.unitWidth + this.gutter) * 2,
31930 y : y + (this.unitWidth + this.gutter) * 2
31937 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31941 if(box[0].size == 'md-left'){
31943 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31950 if(box[0].size == 'md-right'){
31952 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31953 y : minY + (this.unitWidth + this.gutter) * 1
31959 var rand = Math.floor(Math.random() * (4 - box[0].y));
31962 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31963 y : minY + (this.unitWidth + this.gutter) * rand
31970 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31974 if(box[0].size == 'xs'){
31977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31982 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31983 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31991 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31997 y : minY + (this.unitWidth + this.gutter) * 2
32004 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32008 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32011 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32016 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32017 y : minY + (this.unitWidth + this.gutter) * 1
32021 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32022 y : minY + (this.unitWidth + this.gutter) * 2
32029 if(box[0].size == 'xs' && box[1].size == 'xs'){
32032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32042 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32043 y : minY + (this.unitWidth + this.gutter) * 1
32051 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32056 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32057 y : minY + (this.unitWidth + this.gutter) * 2
32061 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32062 y : minY + (this.unitWidth + this.gutter) * 2
32069 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32073 if(box[0].size == 'xs'){
32076 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32081 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32086 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),
32091 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32092 y : minY + (this.unitWidth + this.gutter) * 1
32100 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32105 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32106 y : minY + (this.unitWidth + this.gutter) * 2
32110 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32111 y : minY + (this.unitWidth + this.gutter) * 2
32115 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),
32116 y : minY + (this.unitWidth + this.gutter) * 2
32124 * remove a Masonry Brick
32125 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32127 removeBrick : function(brick_id)
32133 for (var i = 0; i<this.bricks.length; i++) {
32134 if (this.bricks[i].id == brick_id) {
32135 this.bricks.splice(i,1);
32136 this.el.dom.removeChild(Roo.get(brick_id).dom);
32143 * adds a Masonry Brick
32144 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32146 addBrick : function(cfg)
32148 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32149 //this.register(cn);
32150 cn.parentId = this.id;
32151 cn.render(this.el);
32156 * register a Masonry Brick
32157 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32160 register : function(brick)
32162 this.bricks.push(brick);
32163 brick.masonryId = this.id;
32167 * clear all the Masonry Brick
32169 clearAll : function()
32172 //this.getChildContainer().dom.innerHTML = "";
32173 this.el.dom.innerHTML = '';
32176 getSelected : function()
32178 if (!this.selectedBrick) {
32182 return this.selectedBrick;
32186 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32190 * register a Masonry Layout
32191 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32194 register : function(layout)
32196 this.groups[layout.id] = layout;
32199 * fetch a Masonry Layout based on the masonry layout ID
32200 * @param {string} the masonry layout to add
32201 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32204 get: function(layout_id) {
32205 if (typeof(this.groups[layout_id]) == 'undefined') {
32208 return this.groups[layout_id] ;
32220 * http://masonry.desandro.com
32222 * The idea is to render all the bricks based on vertical width...
32224 * The original code extends 'outlayer' - we might need to use that....
32230 * @class Roo.bootstrap.LayoutMasonryAuto
32231 * @extends Roo.bootstrap.Component
32232 * Bootstrap Layout Masonry class
32235 * Create a new Element
32236 * @param {Object} config The config object
32239 Roo.bootstrap.LayoutMasonryAuto = function(config){
32240 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32243 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32246 * @cfg {Boolean} isFitWidth - resize the width..
32248 isFitWidth : false, // options..
32250 * @cfg {Boolean} isOriginLeft = left align?
32252 isOriginLeft : true,
32254 * @cfg {Boolean} isOriginTop = top align?
32256 isOriginTop : false,
32258 * @cfg {Boolean} isLayoutInstant = no animation?
32260 isLayoutInstant : false, // needed?
32262 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32264 isResizingContainer : true,
32266 * @cfg {Number} columnWidth width of the columns
32272 * @cfg {Number} maxCols maximum number of columns
32277 * @cfg {Number} padHeight padding below box..
32283 * @cfg {Boolean} isAutoInitial defalut true
32286 isAutoInitial : true,
32292 initialColumnWidth : 0,
32293 currentSize : null,
32295 colYs : null, // array.
32302 bricks: null, //CompositeElement
32303 cols : 0, // array?
32304 // element : null, // wrapped now this.el
32305 _isLayoutInited : null,
32308 getAutoCreate : function(){
32312 cls: 'blog-masonary-wrapper ' + this.cls,
32314 cls : 'mas-boxes masonary'
32321 getChildContainer: function( )
32323 if (this.boxesEl) {
32324 return this.boxesEl;
32327 this.boxesEl = this.el.select('.mas-boxes').first();
32329 return this.boxesEl;
32333 initEvents : function()
32337 if(this.isAutoInitial){
32338 Roo.log('hook children rendered');
32339 this.on('childrenrendered', function() {
32340 Roo.log('children rendered');
32347 initial : function()
32349 this.reloadItems();
32351 this.currentSize = this.el.getBox(true);
32353 /// was window resize... - let's see if this works..
32354 Roo.EventManager.onWindowResize(this.resize, this);
32356 if(!this.isAutoInitial){
32361 this.layout.defer(500,this);
32364 reloadItems: function()
32366 this.bricks = this.el.select('.masonry-brick', true);
32368 this.bricks.each(function(b) {
32369 //Roo.log(b.getSize());
32370 if (!b.attr('originalwidth')) {
32371 b.attr('originalwidth', b.getSize().width);
32376 Roo.log(this.bricks.elements.length);
32379 resize : function()
32382 var cs = this.el.getBox(true);
32384 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32385 Roo.log("no change in with or X");
32388 this.currentSize = cs;
32392 layout : function()
32395 this._resetLayout();
32396 //this._manageStamps();
32398 // don't animate first layout
32399 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32400 this.layoutItems( isInstant );
32402 // flag for initalized
32403 this._isLayoutInited = true;
32406 layoutItems : function( isInstant )
32408 //var items = this._getItemsForLayout( this.items );
32409 // original code supports filtering layout items.. we just ignore it..
32411 this._layoutItems( this.bricks , isInstant );
32413 this._postLayout();
32415 _layoutItems : function ( items , isInstant)
32417 //this.fireEvent( 'layout', this, items );
32420 if ( !items || !items.elements.length ) {
32421 // no items, emit event with empty array
32426 items.each(function(item) {
32427 Roo.log("layout item");
32429 // get x/y object from method
32430 var position = this._getItemLayoutPosition( item );
32432 position.item = item;
32433 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32434 queue.push( position );
32437 this._processLayoutQueue( queue );
32439 /** Sets position of item in DOM
32440 * @param {Element} item
32441 * @param {Number} x - horizontal position
32442 * @param {Number} y - vertical position
32443 * @param {Boolean} isInstant - disables transitions
32445 _processLayoutQueue : function( queue )
32447 for ( var i=0, len = queue.length; i < len; i++ ) {
32448 var obj = queue[i];
32449 obj.item.position('absolute');
32450 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32456 * Any logic you want to do after each layout,
32457 * i.e. size the container
32459 _postLayout : function()
32461 this.resizeContainer();
32464 resizeContainer : function()
32466 if ( !this.isResizingContainer ) {
32469 var size = this._getContainerSize();
32471 this.el.setSize(size.width,size.height);
32472 this.boxesEl.setSize(size.width,size.height);
32478 _resetLayout : function()
32480 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32481 this.colWidth = this.el.getWidth();
32482 //this.gutter = this.el.getWidth();
32484 this.measureColumns();
32490 this.colYs.push( 0 );
32496 measureColumns : function()
32498 this.getContainerWidth();
32499 // if columnWidth is 0, default to outerWidth of first item
32500 if ( !this.columnWidth ) {
32501 var firstItem = this.bricks.first();
32502 Roo.log(firstItem);
32503 this.columnWidth = this.containerWidth;
32504 if (firstItem && firstItem.attr('originalwidth') ) {
32505 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32507 // columnWidth fall back to item of first element
32508 Roo.log("set column width?");
32509 this.initialColumnWidth = this.columnWidth ;
32511 // if first elem has no width, default to size of container
32516 if (this.initialColumnWidth) {
32517 this.columnWidth = this.initialColumnWidth;
32522 // column width is fixed at the top - however if container width get's smaller we should
32525 // this bit calcs how man columns..
32527 var columnWidth = this.columnWidth += this.gutter;
32529 // calculate columns
32530 var containerWidth = this.containerWidth + this.gutter;
32532 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32533 // fix rounding errors, typically with gutters
32534 var excess = columnWidth - containerWidth % columnWidth;
32537 // if overshoot is less than a pixel, round up, otherwise floor it
32538 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32539 cols = Math[ mathMethod ]( cols );
32540 this.cols = Math.max( cols, 1 );
32541 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32543 // padding positioning..
32544 var totalColWidth = this.cols * this.columnWidth;
32545 var padavail = this.containerWidth - totalColWidth;
32546 // so for 2 columns - we need 3 'pads'
32548 var padNeeded = (1+this.cols) * this.padWidth;
32550 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32552 this.columnWidth += padExtra
32553 //this.padWidth = Math.floor(padavail / ( this.cols));
32555 // adjust colum width so that padding is fixed??
32557 // we have 3 columns ... total = width * 3
32558 // we have X left over... that should be used by
32560 //if (this.expandC) {
32568 getContainerWidth : function()
32570 /* // container is parent if fit width
32571 var container = this.isFitWidth ? this.element.parentNode : this.element;
32572 // check that this.size and size are there
32573 // IE8 triggers resize on body size change, so they might not be
32575 var size = getSize( container ); //FIXME
32576 this.containerWidth = size && size.innerWidth; //FIXME
32579 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32583 _getItemLayoutPosition : function( item ) // what is item?
32585 // we resize the item to our columnWidth..
32587 item.setWidth(this.columnWidth);
32588 item.autoBoxAdjust = false;
32590 var sz = item.getSize();
32592 // how many columns does this brick span
32593 var remainder = this.containerWidth % this.columnWidth;
32595 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32596 // round if off by 1 pixel, otherwise use ceil
32597 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32598 colSpan = Math.min( colSpan, this.cols );
32600 // normally this should be '1' as we dont' currently allow multi width columns..
32602 var colGroup = this._getColGroup( colSpan );
32603 // get the minimum Y value from the columns
32604 var minimumY = Math.min.apply( Math, colGroup );
32605 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32607 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32609 // position the brick
32611 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32612 y: this.currentSize.y + minimumY + this.padHeight
32616 // apply setHeight to necessary columns
32617 var setHeight = minimumY + sz.height + this.padHeight;
32618 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32620 var setSpan = this.cols + 1 - colGroup.length;
32621 for ( var i = 0; i < setSpan; i++ ) {
32622 this.colYs[ shortColIndex + i ] = setHeight ;
32629 * @param {Number} colSpan - number of columns the element spans
32630 * @returns {Array} colGroup
32632 _getColGroup : function( colSpan )
32634 if ( colSpan < 2 ) {
32635 // if brick spans only one column, use all the column Ys
32640 // how many different places could this brick fit horizontally
32641 var groupCount = this.cols + 1 - colSpan;
32642 // for each group potential horizontal position
32643 for ( var i = 0; i < groupCount; i++ ) {
32644 // make an array of colY values for that one group
32645 var groupColYs = this.colYs.slice( i, i + colSpan );
32646 // and get the max value of the array
32647 colGroup[i] = Math.max.apply( Math, groupColYs );
32652 _manageStamp : function( stamp )
32654 var stampSize = stamp.getSize();
32655 var offset = stamp.getBox();
32656 // get the columns that this stamp affects
32657 var firstX = this.isOriginLeft ? offset.x : offset.right;
32658 var lastX = firstX + stampSize.width;
32659 var firstCol = Math.floor( firstX / this.columnWidth );
32660 firstCol = Math.max( 0, firstCol );
32662 var lastCol = Math.floor( lastX / this.columnWidth );
32663 // lastCol should not go over if multiple of columnWidth #425
32664 lastCol -= lastX % this.columnWidth ? 0 : 1;
32665 lastCol = Math.min( this.cols - 1, lastCol );
32667 // set colYs to bottom of the stamp
32668 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32671 for ( var i = firstCol; i <= lastCol; i++ ) {
32672 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32677 _getContainerSize : function()
32679 this.maxY = Math.max.apply( Math, this.colYs );
32684 if ( this.isFitWidth ) {
32685 size.width = this._getContainerFitWidth();
32691 _getContainerFitWidth : function()
32693 var unusedCols = 0;
32694 // count unused columns
32697 if ( this.colYs[i] !== 0 ) {
32702 // fit container to columns that have been used
32703 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32706 needsResizeLayout : function()
32708 var previousWidth = this.containerWidth;
32709 this.getContainerWidth();
32710 return previousWidth !== this.containerWidth;
32725 * @class Roo.bootstrap.MasonryBrick
32726 * @extends Roo.bootstrap.Component
32727 * Bootstrap MasonryBrick class
32730 * Create a new MasonryBrick
32731 * @param {Object} config The config object
32734 Roo.bootstrap.MasonryBrick = function(config){
32736 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32738 Roo.bootstrap.MasonryBrick.register(this);
32744 * When a MasonryBrick is clcik
32745 * @param {Roo.bootstrap.MasonryBrick} this
32746 * @param {Roo.EventObject} e
32752 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32755 * @cfg {String} title
32759 * @cfg {String} html
32763 * @cfg {String} bgimage
32767 * @cfg {String} videourl
32771 * @cfg {String} cls
32775 * @cfg {String} href
32779 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32784 * @cfg {String} placetitle (center|bottom)
32789 * @cfg {Boolean} isFitContainer defalut true
32791 isFitContainer : true,
32794 * @cfg {Boolean} preventDefault defalut false
32796 preventDefault : false,
32799 * @cfg {Boolean} inverse defalut false
32801 maskInverse : false,
32803 getAutoCreate : function()
32805 if(!this.isFitContainer){
32806 return this.getSplitAutoCreate();
32809 var cls = 'masonry-brick masonry-brick-full';
32811 if(this.href.length){
32812 cls += ' masonry-brick-link';
32815 if(this.bgimage.length){
32816 cls += ' masonry-brick-image';
32819 if(this.maskInverse){
32820 cls += ' mask-inverse';
32823 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32824 cls += ' enable-mask';
32828 cls += ' masonry-' + this.size + '-brick';
32831 if(this.placetitle.length){
32833 switch (this.placetitle) {
32835 cls += ' masonry-center-title';
32838 cls += ' masonry-bottom-title';
32845 if(!this.html.length && !this.bgimage.length){
32846 cls += ' masonry-center-title';
32849 if(!this.html.length && this.bgimage.length){
32850 cls += ' masonry-bottom-title';
32855 cls += ' ' + this.cls;
32859 tag: (this.href.length) ? 'a' : 'div',
32864 cls: 'masonry-brick-mask'
32868 cls: 'masonry-brick-paragraph',
32874 if(this.href.length){
32875 cfg.href = this.href;
32878 var cn = cfg.cn[1].cn;
32880 if(this.title.length){
32883 cls: 'masonry-brick-title',
32888 if(this.html.length){
32891 cls: 'masonry-brick-text',
32896 if (!this.title.length && !this.html.length) {
32897 cfg.cn[1].cls += ' hide';
32900 if(this.bgimage.length){
32903 cls: 'masonry-brick-image-view',
32908 if(this.videourl.length){
32909 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32910 // youtube support only?
32913 cls: 'masonry-brick-image-view',
32916 allowfullscreen : true
32924 getSplitAutoCreate : function()
32926 var cls = 'masonry-brick masonry-brick-split';
32928 if(this.href.length){
32929 cls += ' masonry-brick-link';
32932 if(this.bgimage.length){
32933 cls += ' masonry-brick-image';
32937 cls += ' masonry-' + this.size + '-brick';
32940 switch (this.placetitle) {
32942 cls += ' masonry-center-title';
32945 cls += ' masonry-bottom-title';
32948 if(!this.bgimage.length){
32949 cls += ' masonry-center-title';
32952 if(this.bgimage.length){
32953 cls += ' masonry-bottom-title';
32959 cls += ' ' + this.cls;
32963 tag: (this.href.length) ? 'a' : 'div',
32968 cls: 'masonry-brick-split-head',
32972 cls: 'masonry-brick-paragraph',
32979 cls: 'masonry-brick-split-body',
32985 if(this.href.length){
32986 cfg.href = this.href;
32989 if(this.title.length){
32990 cfg.cn[0].cn[0].cn.push({
32992 cls: 'masonry-brick-title',
32997 if(this.html.length){
32998 cfg.cn[1].cn.push({
33000 cls: 'masonry-brick-text',
33005 if(this.bgimage.length){
33006 cfg.cn[0].cn.push({
33008 cls: 'masonry-brick-image-view',
33013 if(this.videourl.length){
33014 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33015 // youtube support only?
33016 cfg.cn[0].cn.cn.push({
33018 cls: 'masonry-brick-image-view',
33021 allowfullscreen : true
33028 initEvents: function()
33030 switch (this.size) {
33063 this.el.on('touchstart', this.onTouchStart, this);
33064 this.el.on('touchmove', this.onTouchMove, this);
33065 this.el.on('touchend', this.onTouchEnd, this);
33066 this.el.on('contextmenu', this.onContextMenu, this);
33068 this.el.on('mouseenter' ,this.enter, this);
33069 this.el.on('mouseleave', this.leave, this);
33070 this.el.on('click', this.onClick, this);
33073 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33074 this.parent().bricks.push(this);
33079 onClick: function(e, el)
33081 var time = this.endTimer - this.startTimer;
33082 // Roo.log(e.preventDefault());
33085 e.preventDefault();
33090 if(!this.preventDefault){
33094 e.preventDefault();
33096 if (this.activeClass != '') {
33097 this.selectBrick();
33100 this.fireEvent('click', this, e);
33103 enter: function(e, el)
33105 e.preventDefault();
33107 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33111 if(this.bgimage.length && this.html.length){
33112 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33116 leave: function(e, el)
33118 e.preventDefault();
33120 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33124 if(this.bgimage.length && this.html.length){
33125 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33129 onTouchStart: function(e, el)
33131 // e.preventDefault();
33133 this.touchmoved = false;
33135 if(!this.isFitContainer){
33139 if(!this.bgimage.length || !this.html.length){
33143 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33145 this.timer = new Date().getTime();
33149 onTouchMove: function(e, el)
33151 this.touchmoved = true;
33154 onContextMenu : function(e,el)
33156 e.preventDefault();
33157 e.stopPropagation();
33161 onTouchEnd: function(e, el)
33163 // e.preventDefault();
33165 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33172 if(!this.bgimage.length || !this.html.length){
33174 if(this.href.length){
33175 window.location.href = this.href;
33181 if(!this.isFitContainer){
33185 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33187 window.location.href = this.href;
33190 //selection on single brick only
33191 selectBrick : function() {
33193 if (!this.parentId) {
33197 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33198 var index = m.selectedBrick.indexOf(this.id);
33201 m.selectedBrick.splice(index,1);
33202 this.el.removeClass(this.activeClass);
33206 for(var i = 0; i < m.selectedBrick.length; i++) {
33207 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33208 b.el.removeClass(b.activeClass);
33211 m.selectedBrick = [];
33213 m.selectedBrick.push(this.id);
33214 this.el.addClass(this.activeClass);
33218 isSelected : function(){
33219 return this.el.hasClass(this.activeClass);
33224 Roo.apply(Roo.bootstrap.MasonryBrick, {
33227 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33229 * register a Masonry Brick
33230 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33233 register : function(brick)
33235 //this.groups[brick.id] = brick;
33236 this.groups.add(brick.id, brick);
33239 * fetch a masonry brick based on the masonry brick ID
33240 * @param {string} the masonry brick to add
33241 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33244 get: function(brick_id)
33246 // if (typeof(this.groups[brick_id]) == 'undefined') {
33249 // return this.groups[brick_id] ;
33251 if(this.groups.key(brick_id)) {
33252 return this.groups.key(brick_id);
33270 * @class Roo.bootstrap.Brick
33271 * @extends Roo.bootstrap.Component
33272 * Bootstrap Brick class
33275 * Create a new Brick
33276 * @param {Object} config The config object
33279 Roo.bootstrap.Brick = function(config){
33280 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33286 * When a Brick is click
33287 * @param {Roo.bootstrap.Brick} this
33288 * @param {Roo.EventObject} e
33294 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33297 * @cfg {String} title
33301 * @cfg {String} html
33305 * @cfg {String} bgimage
33309 * @cfg {String} cls
33313 * @cfg {String} href
33317 * @cfg {String} video
33321 * @cfg {Boolean} square
33325 getAutoCreate : function()
33327 var cls = 'roo-brick';
33329 if(this.href.length){
33330 cls += ' roo-brick-link';
33333 if(this.bgimage.length){
33334 cls += ' roo-brick-image';
33337 if(!this.html.length && !this.bgimage.length){
33338 cls += ' roo-brick-center-title';
33341 if(!this.html.length && this.bgimage.length){
33342 cls += ' roo-brick-bottom-title';
33346 cls += ' ' + this.cls;
33350 tag: (this.href.length) ? 'a' : 'div',
33355 cls: 'roo-brick-paragraph',
33361 if(this.href.length){
33362 cfg.href = this.href;
33365 var cn = cfg.cn[0].cn;
33367 if(this.title.length){
33370 cls: 'roo-brick-title',
33375 if(this.html.length){
33378 cls: 'roo-brick-text',
33385 if(this.bgimage.length){
33388 cls: 'roo-brick-image-view',
33396 initEvents: function()
33398 if(this.title.length || this.html.length){
33399 this.el.on('mouseenter' ,this.enter, this);
33400 this.el.on('mouseleave', this.leave, this);
33403 Roo.EventManager.onWindowResize(this.resize, this);
33405 if(this.bgimage.length){
33406 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33407 this.imageEl.on('load', this.onImageLoad, this);
33414 onImageLoad : function()
33419 resize : function()
33421 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33423 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33425 if(this.bgimage.length){
33426 var image = this.el.select('.roo-brick-image-view', true).first();
33428 image.setWidth(paragraph.getWidth());
33431 image.setHeight(paragraph.getWidth());
33434 this.el.setHeight(image.getHeight());
33435 paragraph.setHeight(image.getHeight());
33441 enter: function(e, el)
33443 e.preventDefault();
33445 if(this.bgimage.length){
33446 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33447 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33451 leave: function(e, el)
33453 e.preventDefault();
33455 if(this.bgimage.length){
33456 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33457 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33472 * @class Roo.bootstrap.NumberField
33473 * @extends Roo.bootstrap.Input
33474 * Bootstrap NumberField class
33480 * Create a new NumberField
33481 * @param {Object} config The config object
33484 Roo.bootstrap.NumberField = function(config){
33485 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33488 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33491 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33493 allowDecimals : true,
33495 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33497 decimalSeparator : ".",
33499 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33501 decimalPrecision : 2,
33503 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33505 allowNegative : true,
33508 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33512 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33514 minValue : Number.NEGATIVE_INFINITY,
33516 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33518 maxValue : Number.MAX_VALUE,
33520 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33522 minText : "The minimum value for this field is {0}",
33524 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33526 maxText : "The maximum value for this field is {0}",
33528 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33529 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33531 nanText : "{0} is not a valid number",
33533 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33535 thousandsDelimiter : false,
33537 * @cfg {String} valueAlign alignment of value
33539 valueAlign : "left",
33541 getAutoCreate : function()
33543 var hiddenInput = {
33547 cls: 'hidden-number-input'
33551 hiddenInput.name = this.name;
33556 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33558 this.name = hiddenInput.name;
33560 if(cfg.cn.length > 0) {
33561 cfg.cn.push(hiddenInput);
33568 initEvents : function()
33570 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33572 var allowed = "0123456789";
33574 if(this.allowDecimals){
33575 allowed += this.decimalSeparator;
33578 if(this.allowNegative){
33582 if(this.thousandsDelimiter) {
33586 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33588 var keyPress = function(e){
33590 var k = e.getKey();
33592 var c = e.getCharCode();
33595 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33596 allowed.indexOf(String.fromCharCode(c)) === -1
33602 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33606 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33611 this.el.on("keypress", keyPress, this);
33614 validateValue : function(value)
33617 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33621 var num = this.parseValue(value);
33624 this.markInvalid(String.format(this.nanText, value));
33628 if(num < this.minValue){
33629 this.markInvalid(String.format(this.minText, this.minValue));
33633 if(num > this.maxValue){
33634 this.markInvalid(String.format(this.maxText, this.maxValue));
33641 getValue : function()
33643 var v = this.hiddenEl().getValue();
33645 return this.fixPrecision(this.parseValue(v));
33648 parseValue : function(value)
33650 if(this.thousandsDelimiter) {
33652 r = new RegExp(",", "g");
33653 value = value.replace(r, "");
33656 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33657 return isNaN(value) ? '' : value;
33660 fixPrecision : function(value)
33662 if(this.thousandsDelimiter) {
33664 r = new RegExp(",", "g");
33665 value = value.replace(r, "");
33668 var nan = isNaN(value);
33670 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33671 return nan ? '' : value;
33673 return parseFloat(value).toFixed(this.decimalPrecision);
33676 setValue : function(v)
33678 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33684 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33686 this.inputEl().dom.value = (v == '') ? '' :
33687 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33689 if(!this.allowZero && v === '0') {
33690 this.hiddenEl().dom.value = '';
33691 this.inputEl().dom.value = '';
33698 decimalPrecisionFcn : function(v)
33700 return Math.floor(v);
33703 beforeBlur : function()
33705 var v = this.parseValue(this.getRawValue());
33707 if(v || v === 0 || v === ''){
33712 hiddenEl : function()
33714 return this.el.select('input.hidden-number-input',true).first();
33726 * @class Roo.bootstrap.DocumentSlider
33727 * @extends Roo.bootstrap.Component
33728 * Bootstrap DocumentSlider class
33731 * Create a new DocumentViewer
33732 * @param {Object} config The config object
33735 Roo.bootstrap.DocumentSlider = function(config){
33736 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33743 * Fire after initEvent
33744 * @param {Roo.bootstrap.DocumentSlider} this
33749 * Fire after update
33750 * @param {Roo.bootstrap.DocumentSlider} this
33756 * @param {Roo.bootstrap.DocumentSlider} this
33762 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33768 getAutoCreate : function()
33772 cls : 'roo-document-slider',
33776 cls : 'roo-document-slider-header',
33780 cls : 'roo-document-slider-header-title'
33786 cls : 'roo-document-slider-body',
33790 cls : 'roo-document-slider-prev',
33794 cls : 'fa fa-chevron-left'
33800 cls : 'roo-document-slider-thumb',
33804 cls : 'roo-document-slider-image'
33810 cls : 'roo-document-slider-next',
33814 cls : 'fa fa-chevron-right'
33826 initEvents : function()
33828 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33829 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33831 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33832 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33834 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33835 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33837 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33838 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33840 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33841 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33843 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33844 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33846 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33847 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33849 this.thumbEl.on('click', this.onClick, this);
33851 this.prevIndicator.on('click', this.prev, this);
33853 this.nextIndicator.on('click', this.next, this);
33857 initial : function()
33859 if(this.files.length){
33860 this.indicator = 1;
33864 this.fireEvent('initial', this);
33867 update : function()
33869 this.imageEl.attr('src', this.files[this.indicator - 1]);
33871 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33873 this.prevIndicator.show();
33875 if(this.indicator == 1){
33876 this.prevIndicator.hide();
33879 this.nextIndicator.show();
33881 if(this.indicator == this.files.length){
33882 this.nextIndicator.hide();
33885 this.thumbEl.scrollTo('top');
33887 this.fireEvent('update', this);
33890 onClick : function(e)
33892 e.preventDefault();
33894 this.fireEvent('click', this);
33899 e.preventDefault();
33901 this.indicator = Math.max(1, this.indicator - 1);
33908 e.preventDefault();
33910 this.indicator = Math.min(this.files.length, this.indicator + 1);
33924 * @class Roo.bootstrap.RadioSet
33925 * @extends Roo.bootstrap.Input
33926 * Bootstrap RadioSet class
33927 * @cfg {String} indicatorpos (left|right) default left
33928 * @cfg {Boolean} inline (true|false) inline the element (default true)
33929 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33931 * Create a new RadioSet
33932 * @param {Object} config The config object
33935 Roo.bootstrap.RadioSet = function(config){
33937 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33941 Roo.bootstrap.RadioSet.register(this);
33946 * Fires when the element is checked or unchecked.
33947 * @param {Roo.bootstrap.RadioSet} this This radio
33948 * @param {Roo.bootstrap.Radio} item The checked item
33953 * Fires when the element is click.
33954 * @param {Roo.bootstrap.RadioSet} this This radio set
33955 * @param {Roo.bootstrap.Radio} item The checked item
33956 * @param {Roo.EventObject} e The event object
33963 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33971 indicatorpos : 'left',
33973 getAutoCreate : function()
33977 cls : 'roo-radio-set-label',
33981 html : this.fieldLabel
33986 if(this.indicatorpos == 'left'){
33989 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33990 tooltip : 'This field is required'
33995 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33996 tooltip : 'This field is required'
34002 cls : 'roo-radio-set-items'
34005 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34007 if (align === 'left' && this.fieldLabel.length) {
34010 cls : "roo-radio-set-right",
34016 if(this.labelWidth > 12){
34017 label.style = "width: " + this.labelWidth + 'px';
34020 if(this.labelWidth < 13 && this.labelmd == 0){
34021 this.labelmd = this.labelWidth;
34024 if(this.labellg > 0){
34025 label.cls += ' col-lg-' + this.labellg;
34026 items.cls += ' col-lg-' + (12 - this.labellg);
34029 if(this.labelmd > 0){
34030 label.cls += ' col-md-' + this.labelmd;
34031 items.cls += ' col-md-' + (12 - this.labelmd);
34034 if(this.labelsm > 0){
34035 label.cls += ' col-sm-' + this.labelsm;
34036 items.cls += ' col-sm-' + (12 - this.labelsm);
34039 if(this.labelxs > 0){
34040 label.cls += ' col-xs-' + this.labelxs;
34041 items.cls += ' col-xs-' + (12 - this.labelxs);
34047 cls : 'roo-radio-set',
34051 cls : 'roo-radio-set-input',
34054 value : this.value ? this.value : ''
34061 if(this.weight.length){
34062 cfg.cls += ' roo-radio-' + this.weight;
34066 cfg.cls += ' roo-radio-set-inline';
34070 ['xs','sm','md','lg'].map(function(size){
34071 if (settings[size]) {
34072 cfg.cls += ' col-' + size + '-' + settings[size];
34080 initEvents : function()
34082 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34083 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34085 if(!this.fieldLabel.length){
34086 this.labelEl.hide();
34089 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34090 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34092 this.indicator = this.indicatorEl();
34094 if(this.indicator){
34095 this.indicator.addClass('invisible');
34098 this.originalValue = this.getValue();
34102 inputEl: function ()
34104 return this.el.select('.roo-radio-set-input', true).first();
34107 getChildContainer : function()
34109 return this.itemsEl;
34112 register : function(item)
34114 this.radioes.push(item);
34118 validate : function()
34120 if(this.getVisibilityEl().hasClass('hidden')){
34126 Roo.each(this.radioes, function(i){
34135 if(this.allowBlank) {
34139 if(this.disabled || valid){
34144 this.markInvalid();
34149 markValid : function()
34151 if(this.labelEl.isVisible(true)){
34152 this.indicatorEl().removeClass('visible');
34153 this.indicatorEl().addClass('invisible');
34156 this.el.removeClass([this.invalidClass, this.validClass]);
34157 this.el.addClass(this.validClass);
34159 this.fireEvent('valid', this);
34162 markInvalid : function(msg)
34164 if(this.allowBlank || this.disabled){
34168 if(this.labelEl.isVisible(true)){
34169 this.indicatorEl().removeClass('invisible');
34170 this.indicatorEl().addClass('visible');
34173 this.el.removeClass([this.invalidClass, this.validClass]);
34174 this.el.addClass(this.invalidClass);
34176 this.fireEvent('invalid', this, msg);
34180 setValue : function(v, suppressEvent)
34182 if(this.value === v){
34189 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34192 Roo.each(this.radioes, function(i){
34194 i.el.removeClass('checked');
34197 Roo.each(this.radioes, function(i){
34199 if(i.value === v || i.value.toString() === v.toString()){
34201 i.el.addClass('checked');
34203 if(suppressEvent !== true){
34204 this.fireEvent('check', this, i);
34215 clearInvalid : function(){
34217 if(!this.el || this.preventMark){
34221 this.el.removeClass([this.invalidClass]);
34223 this.fireEvent('valid', this);
34228 Roo.apply(Roo.bootstrap.RadioSet, {
34232 register : function(set)
34234 this.groups[set.name] = set;
34237 get: function(name)
34239 if (typeof(this.groups[name]) == 'undefined') {
34243 return this.groups[name] ;
34249 * Ext JS Library 1.1.1
34250 * Copyright(c) 2006-2007, Ext JS, LLC.
34252 * Originally Released Under LGPL - original licence link has changed is not relivant.
34255 * <script type="text/javascript">
34260 * @class Roo.bootstrap.SplitBar
34261 * @extends Roo.util.Observable
34262 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34266 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34267 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34268 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34269 split.minSize = 100;
34270 split.maxSize = 600;
34271 split.animate = true;
34272 split.on('moved', splitterMoved);
34275 * Create a new SplitBar
34276 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34277 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34278 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34279 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34280 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34281 position of the SplitBar).
34283 Roo.bootstrap.SplitBar = function(cfg){
34288 // dragElement : elm
34289 // resizingElement: el,
34291 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34292 // placement : Roo.bootstrap.SplitBar.LEFT ,
34293 // existingProxy ???
34296 this.el = Roo.get(cfg.dragElement, true);
34297 this.el.dom.unselectable = "on";
34299 this.resizingEl = Roo.get(cfg.resizingElement, true);
34303 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34304 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34307 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34310 * The minimum size of the resizing element. (Defaults to 0)
34316 * The maximum size of the resizing element. (Defaults to 2000)
34319 this.maxSize = 2000;
34322 * Whether to animate the transition to the new size
34325 this.animate = false;
34328 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34331 this.useShim = false;
34336 if(!cfg.existingProxy){
34338 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34340 this.proxy = Roo.get(cfg.existingProxy).dom;
34343 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34346 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34349 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34352 this.dragSpecs = {};
34355 * @private The adapter to use to positon and resize elements
34357 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34358 this.adapter.init(this);
34360 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34362 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34363 this.el.addClass("roo-splitbar-h");
34366 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34367 this.el.addClass("roo-splitbar-v");
34373 * Fires when the splitter is moved (alias for {@link #event-moved})
34374 * @param {Roo.bootstrap.SplitBar} this
34375 * @param {Number} newSize the new width or height
34380 * Fires when the splitter is moved
34381 * @param {Roo.bootstrap.SplitBar} this
34382 * @param {Number} newSize the new width or height
34386 * @event beforeresize
34387 * Fires before the splitter is dragged
34388 * @param {Roo.bootstrap.SplitBar} this
34390 "beforeresize" : true,
34392 "beforeapply" : true
34395 Roo.util.Observable.call(this);
34398 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34399 onStartProxyDrag : function(x, y){
34400 this.fireEvent("beforeresize", this);
34402 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34404 o.enableDisplayMode("block");
34405 // all splitbars share the same overlay
34406 Roo.bootstrap.SplitBar.prototype.overlay = o;
34408 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34409 this.overlay.show();
34410 Roo.get(this.proxy).setDisplayed("block");
34411 var size = this.adapter.getElementSize(this);
34412 this.activeMinSize = this.getMinimumSize();;
34413 this.activeMaxSize = this.getMaximumSize();;
34414 var c1 = size - this.activeMinSize;
34415 var c2 = Math.max(this.activeMaxSize - size, 0);
34416 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34417 this.dd.resetConstraints();
34418 this.dd.setXConstraint(
34419 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34420 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34422 this.dd.setYConstraint(0, 0);
34424 this.dd.resetConstraints();
34425 this.dd.setXConstraint(0, 0);
34426 this.dd.setYConstraint(
34427 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34428 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34431 this.dragSpecs.startSize = size;
34432 this.dragSpecs.startPoint = [x, y];
34433 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34437 * @private Called after the drag operation by the DDProxy
34439 onEndProxyDrag : function(e){
34440 Roo.get(this.proxy).setDisplayed(false);
34441 var endPoint = Roo.lib.Event.getXY(e);
34443 this.overlay.hide();
34446 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34447 newSize = this.dragSpecs.startSize +
34448 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34449 endPoint[0] - this.dragSpecs.startPoint[0] :
34450 this.dragSpecs.startPoint[0] - endPoint[0]
34453 newSize = this.dragSpecs.startSize +
34454 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34455 endPoint[1] - this.dragSpecs.startPoint[1] :
34456 this.dragSpecs.startPoint[1] - endPoint[1]
34459 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34460 if(newSize != this.dragSpecs.startSize){
34461 if(this.fireEvent('beforeapply', this, newSize) !== false){
34462 this.adapter.setElementSize(this, newSize);
34463 this.fireEvent("moved", this, newSize);
34464 this.fireEvent("resize", this, newSize);
34470 * Get the adapter this SplitBar uses
34471 * @return The adapter object
34473 getAdapter : function(){
34474 return this.adapter;
34478 * Set the adapter this SplitBar uses
34479 * @param {Object} adapter A SplitBar adapter object
34481 setAdapter : function(adapter){
34482 this.adapter = adapter;
34483 this.adapter.init(this);
34487 * Gets the minimum size for the resizing element
34488 * @return {Number} The minimum size
34490 getMinimumSize : function(){
34491 return this.minSize;
34495 * Sets the minimum size for the resizing element
34496 * @param {Number} minSize The minimum size
34498 setMinimumSize : function(minSize){
34499 this.minSize = minSize;
34503 * Gets the maximum size for the resizing element
34504 * @return {Number} The maximum size
34506 getMaximumSize : function(){
34507 return this.maxSize;
34511 * Sets the maximum size for the resizing element
34512 * @param {Number} maxSize The maximum size
34514 setMaximumSize : function(maxSize){
34515 this.maxSize = maxSize;
34519 * Sets the initialize size for the resizing element
34520 * @param {Number} size The initial size
34522 setCurrentSize : function(size){
34523 var oldAnimate = this.animate;
34524 this.animate = false;
34525 this.adapter.setElementSize(this, size);
34526 this.animate = oldAnimate;
34530 * Destroy this splitbar.
34531 * @param {Boolean} removeEl True to remove the element
34533 destroy : function(removeEl){
34535 this.shim.remove();
34538 this.proxy.parentNode.removeChild(this.proxy);
34546 * @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.
34548 Roo.bootstrap.SplitBar.createProxy = function(dir){
34549 var proxy = new Roo.Element(document.createElement("div"));
34550 proxy.unselectable();
34551 var cls = 'roo-splitbar-proxy';
34552 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34553 document.body.appendChild(proxy.dom);
34558 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34559 * Default Adapter. It assumes the splitter and resizing element are not positioned
34560 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34562 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34565 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34566 // do nothing for now
34567 init : function(s){
34571 * Called before drag operations to get the current size of the resizing element.
34572 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34574 getElementSize : function(s){
34575 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34576 return s.resizingEl.getWidth();
34578 return s.resizingEl.getHeight();
34583 * Called after drag operations to set the size of the resizing element.
34584 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34585 * @param {Number} newSize The new size to set
34586 * @param {Function} onComplete A function to be invoked when resizing is complete
34588 setElementSize : function(s, newSize, onComplete){
34589 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34591 s.resizingEl.setWidth(newSize);
34593 onComplete(s, newSize);
34596 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34601 s.resizingEl.setHeight(newSize);
34603 onComplete(s, newSize);
34606 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34613 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34614 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34615 * Adapter that moves the splitter element to align with the resized sizing element.
34616 * Used with an absolute positioned SplitBar.
34617 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34618 * document.body, make sure you assign an id to the body element.
34620 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34621 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34622 this.container = Roo.get(container);
34625 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34626 init : function(s){
34627 this.basic.init(s);
34630 getElementSize : function(s){
34631 return this.basic.getElementSize(s);
34634 setElementSize : function(s, newSize, onComplete){
34635 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34638 moveSplitter : function(s){
34639 var yes = Roo.bootstrap.SplitBar;
34640 switch(s.placement){
34642 s.el.setX(s.resizingEl.getRight());
34645 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34648 s.el.setY(s.resizingEl.getBottom());
34651 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34658 * Orientation constant - Create a vertical SplitBar
34662 Roo.bootstrap.SplitBar.VERTICAL = 1;
34665 * Orientation constant - Create a horizontal SplitBar
34669 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34672 * Placement constant - The resizing element is to the left of the splitter element
34676 Roo.bootstrap.SplitBar.LEFT = 1;
34679 * Placement constant - The resizing element is to the right of the splitter element
34683 Roo.bootstrap.SplitBar.RIGHT = 2;
34686 * Placement constant - The resizing element is positioned above the splitter element
34690 Roo.bootstrap.SplitBar.TOP = 3;
34693 * Placement constant - The resizing element is positioned under splitter element
34697 Roo.bootstrap.SplitBar.BOTTOM = 4;
34698 Roo.namespace("Roo.bootstrap.layout");/*
34700 * Ext JS Library 1.1.1
34701 * Copyright(c) 2006-2007, Ext JS, LLC.
34703 * Originally Released Under LGPL - original licence link has changed is not relivant.
34706 * <script type="text/javascript">
34710 * @class Roo.bootstrap.layout.Manager
34711 * @extends Roo.bootstrap.Component
34712 * Base class for layout managers.
34714 Roo.bootstrap.layout.Manager = function(config)
34716 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34722 /** false to disable window resize monitoring @type Boolean */
34723 this.monitorWindowResize = true;
34728 * Fires when a layout is performed.
34729 * @param {Roo.LayoutManager} this
34733 * @event regionresized
34734 * Fires when the user resizes a region.
34735 * @param {Roo.LayoutRegion} region The resized region
34736 * @param {Number} newSize The new size (width for east/west, height for north/south)
34738 "regionresized" : true,
34740 * @event regioncollapsed
34741 * Fires when a region is collapsed.
34742 * @param {Roo.LayoutRegion} region The collapsed region
34744 "regioncollapsed" : true,
34746 * @event regionexpanded
34747 * Fires when a region is expanded.
34748 * @param {Roo.LayoutRegion} region The expanded region
34750 "regionexpanded" : true
34752 this.updating = false;
34755 this.el = Roo.get(config.el);
34761 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34766 monitorWindowResize : true,
34772 onRender : function(ct, position)
34775 this.el = Roo.get(ct);
34778 //this.fireEvent('render',this);
34782 initEvents: function()
34786 // ie scrollbar fix
34787 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34788 document.body.scroll = "no";
34789 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34790 this.el.position('relative');
34792 this.id = this.el.id;
34793 this.el.addClass("roo-layout-container");
34794 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34795 if(this.el.dom != document.body ) {
34796 this.el.on('resize', this.layout,this);
34797 this.el.on('show', this.layout,this);
34803 * Returns true if this layout is currently being updated
34804 * @return {Boolean}
34806 isUpdating : function(){
34807 return this.updating;
34811 * Suspend the LayoutManager from doing auto-layouts while
34812 * making multiple add or remove calls
34814 beginUpdate : function(){
34815 this.updating = true;
34819 * Restore auto-layouts and optionally disable the manager from performing a layout
34820 * @param {Boolean} noLayout true to disable a layout update
34822 endUpdate : function(noLayout){
34823 this.updating = false;
34829 layout: function(){
34833 onRegionResized : function(region, newSize){
34834 this.fireEvent("regionresized", region, newSize);
34838 onRegionCollapsed : function(region){
34839 this.fireEvent("regioncollapsed", region);
34842 onRegionExpanded : function(region){
34843 this.fireEvent("regionexpanded", region);
34847 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34848 * performs box-model adjustments.
34849 * @return {Object} The size as an object {width: (the width), height: (the height)}
34851 getViewSize : function()
34854 if(this.el.dom != document.body){
34855 size = this.el.getSize();
34857 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34859 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34860 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34865 * Returns the Element this layout is bound to.
34866 * @return {Roo.Element}
34868 getEl : function(){
34873 * Returns the specified region.
34874 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34875 * @return {Roo.LayoutRegion}
34877 getRegion : function(target){
34878 return this.regions[target.toLowerCase()];
34881 onWindowResize : function(){
34882 if(this.monitorWindowResize){
34889 * Ext JS Library 1.1.1
34890 * Copyright(c) 2006-2007, Ext JS, LLC.
34892 * Originally Released Under LGPL - original licence link has changed is not relivant.
34895 * <script type="text/javascript">
34898 * @class Roo.bootstrap.layout.Border
34899 * @extends Roo.bootstrap.layout.Manager
34900 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34901 * please see: examples/bootstrap/nested.html<br><br>
34903 <b>The container the layout is rendered into can be either the body element or any other element.
34904 If it is not the body element, the container needs to either be an absolute positioned element,
34905 or you will need to add "position:relative" to the css of the container. You will also need to specify
34906 the container size if it is not the body element.</b>
34909 * Create a new Border
34910 * @param {Object} config Configuration options
34912 Roo.bootstrap.layout.Border = function(config){
34913 config = config || {};
34914 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34918 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34919 if(config[region]){
34920 config[region].region = region;
34921 this.addRegion(config[region]);
34927 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34929 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34931 * Creates and adds a new region if it doesn't already exist.
34932 * @param {String} target The target region key (north, south, east, west or center).
34933 * @param {Object} config The regions config object
34934 * @return {BorderLayoutRegion} The new region
34936 addRegion : function(config)
34938 if(!this.regions[config.region]){
34939 var r = this.factory(config);
34940 this.bindRegion(r);
34942 return this.regions[config.region];
34946 bindRegion : function(r){
34947 this.regions[r.config.region] = r;
34949 r.on("visibilitychange", this.layout, this);
34950 r.on("paneladded", this.layout, this);
34951 r.on("panelremoved", this.layout, this);
34952 r.on("invalidated", this.layout, this);
34953 r.on("resized", this.onRegionResized, this);
34954 r.on("collapsed", this.onRegionCollapsed, this);
34955 r.on("expanded", this.onRegionExpanded, this);
34959 * Performs a layout update.
34961 layout : function()
34963 if(this.updating) {
34967 // render all the rebions if they have not been done alreayd?
34968 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34969 if(this.regions[region] && !this.regions[region].bodyEl){
34970 this.regions[region].onRender(this.el)
34974 var size = this.getViewSize();
34975 var w = size.width;
34976 var h = size.height;
34981 //var x = 0, y = 0;
34983 var rs = this.regions;
34984 var north = rs["north"];
34985 var south = rs["south"];
34986 var west = rs["west"];
34987 var east = rs["east"];
34988 var center = rs["center"];
34989 //if(this.hideOnLayout){ // not supported anymore
34990 //c.el.setStyle("display", "none");
34992 if(north && north.isVisible()){
34993 var b = north.getBox();
34994 var m = north.getMargins();
34995 b.width = w - (m.left+m.right);
34998 centerY = b.height + b.y + m.bottom;
34999 centerH -= centerY;
35000 north.updateBox(this.safeBox(b));
35002 if(south && south.isVisible()){
35003 var b = south.getBox();
35004 var m = south.getMargins();
35005 b.width = w - (m.left+m.right);
35007 var totalHeight = (b.height + m.top + m.bottom);
35008 b.y = h - totalHeight + m.top;
35009 centerH -= totalHeight;
35010 south.updateBox(this.safeBox(b));
35012 if(west && west.isVisible()){
35013 var b = west.getBox();
35014 var m = west.getMargins();
35015 b.height = centerH - (m.top+m.bottom);
35017 b.y = centerY + m.top;
35018 var totalWidth = (b.width + m.left + m.right);
35019 centerX += totalWidth;
35020 centerW -= totalWidth;
35021 west.updateBox(this.safeBox(b));
35023 if(east && east.isVisible()){
35024 var b = east.getBox();
35025 var m = east.getMargins();
35026 b.height = centerH - (m.top+m.bottom);
35027 var totalWidth = (b.width + m.left + m.right);
35028 b.x = w - totalWidth + m.left;
35029 b.y = centerY + m.top;
35030 centerW -= totalWidth;
35031 east.updateBox(this.safeBox(b));
35034 var m = center.getMargins();
35036 x: centerX + m.left,
35037 y: centerY + m.top,
35038 width: centerW - (m.left+m.right),
35039 height: centerH - (m.top+m.bottom)
35041 //if(this.hideOnLayout){
35042 //center.el.setStyle("display", "block");
35044 center.updateBox(this.safeBox(centerBox));
35047 this.fireEvent("layout", this);
35051 safeBox : function(box){
35052 box.width = Math.max(0, box.width);
35053 box.height = Math.max(0, box.height);
35058 * Adds a ContentPanel (or subclass) to this layout.
35059 * @param {String} target The target region key (north, south, east, west or center).
35060 * @param {Roo.ContentPanel} panel The panel to add
35061 * @return {Roo.ContentPanel} The added panel
35063 add : function(target, panel){
35065 target = target.toLowerCase();
35066 return this.regions[target].add(panel);
35070 * Remove a ContentPanel (or subclass) to this layout.
35071 * @param {String} target The target region key (north, south, east, west or center).
35072 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35073 * @return {Roo.ContentPanel} The removed panel
35075 remove : function(target, panel){
35076 target = target.toLowerCase();
35077 return this.regions[target].remove(panel);
35081 * Searches all regions for a panel with the specified id
35082 * @param {String} panelId
35083 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35085 findPanel : function(panelId){
35086 var rs = this.regions;
35087 for(var target in rs){
35088 if(typeof rs[target] != "function"){
35089 var p = rs[target].getPanel(panelId);
35099 * Searches all regions for a panel with the specified id and activates (shows) it.
35100 * @param {String/ContentPanel} panelId The panels id or the panel itself
35101 * @return {Roo.ContentPanel} The shown panel or null
35103 showPanel : function(panelId) {
35104 var rs = this.regions;
35105 for(var target in rs){
35106 var r = rs[target];
35107 if(typeof r != "function"){
35108 if(r.hasPanel(panelId)){
35109 return r.showPanel(panelId);
35117 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35118 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35121 restoreState : function(provider){
35123 provider = Roo.state.Manager;
35125 var sm = new Roo.LayoutStateManager();
35126 sm.init(this, provider);
35132 * Adds a xtype elements to the layout.
35136 xtype : 'ContentPanel',
35143 xtype : 'NestedLayoutPanel',
35149 items : [ ... list of content panels or nested layout panels.. ]
35153 * @param {Object} cfg Xtype definition of item to add.
35155 addxtype : function(cfg)
35157 // basically accepts a pannel...
35158 // can accept a layout region..!?!?
35159 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35162 // theory? children can only be panels??
35164 //if (!cfg.xtype.match(/Panel$/)) {
35169 if (typeof(cfg.region) == 'undefined') {
35170 Roo.log("Failed to add Panel, region was not set");
35174 var region = cfg.region;
35180 xitems = cfg.items;
35187 case 'Content': // ContentPanel (el, cfg)
35188 case 'Scroll': // ContentPanel (el, cfg)
35190 cfg.autoCreate = true;
35191 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35193 // var el = this.el.createChild();
35194 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35197 this.add(region, ret);
35201 case 'TreePanel': // our new panel!
35202 cfg.el = this.el.createChild();
35203 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35204 this.add(region, ret);
35209 // create a new Layout (which is a Border Layout...
35211 var clayout = cfg.layout;
35212 clayout.el = this.el.createChild();
35213 clayout.items = clayout.items || [];
35217 // replace this exitems with the clayout ones..
35218 xitems = clayout.items;
35220 // force background off if it's in center...
35221 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35222 cfg.background = false;
35224 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35227 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35228 //console.log('adding nested layout panel ' + cfg.toSource());
35229 this.add(region, ret);
35230 nb = {}; /// find first...
35235 // needs grid and region
35237 //var el = this.getRegion(region).el.createChild();
35239 *var el = this.el.createChild();
35240 // create the grid first...
35241 cfg.grid.container = el;
35242 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35245 if (region == 'center' && this.active ) {
35246 cfg.background = false;
35249 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35251 this.add(region, ret);
35253 if (cfg.background) {
35254 // render grid on panel activation (if panel background)
35255 ret.on('activate', function(gp) {
35256 if (!gp.grid.rendered) {
35257 // gp.grid.render(el);
35261 // cfg.grid.render(el);
35267 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35268 // it was the old xcomponent building that caused this before.
35269 // espeically if border is the top element in the tree.
35279 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35281 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35282 this.add(region, ret);
35286 throw "Can not add '" + cfg.xtype + "' to Border";
35292 this.beginUpdate();
35296 Roo.each(xitems, function(i) {
35297 region = nb && i.region ? i.region : false;
35299 var add = ret.addxtype(i);
35302 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35303 if (!i.background) {
35304 abn[region] = nb[region] ;
35311 // make the last non-background panel active..
35312 //if (nb) { Roo.log(abn); }
35315 for(var r in abn) {
35316 region = this.getRegion(r);
35318 // tried using nb[r], but it does not work..
35320 region.showPanel(abn[r]);
35331 factory : function(cfg)
35334 var validRegions = Roo.bootstrap.layout.Border.regions;
35336 var target = cfg.region;
35339 var r = Roo.bootstrap.layout;
35343 return new r.North(cfg);
35345 return new r.South(cfg);
35347 return new r.East(cfg);
35349 return new r.West(cfg);
35351 return new r.Center(cfg);
35353 throw 'Layout region "'+target+'" not supported.';
35360 * Ext JS Library 1.1.1
35361 * Copyright(c) 2006-2007, Ext JS, LLC.
35363 * Originally Released Under LGPL - original licence link has changed is not relivant.
35366 * <script type="text/javascript">
35370 * @class Roo.bootstrap.layout.Basic
35371 * @extends Roo.util.Observable
35372 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35373 * and does not have a titlebar, tabs or any other features. All it does is size and position
35374 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35375 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35376 * @cfg {string} region the region that it inhabits..
35377 * @cfg {bool} skipConfig skip config?
35381 Roo.bootstrap.layout.Basic = function(config){
35383 this.mgr = config.mgr;
35385 this.position = config.region;
35387 var skipConfig = config.skipConfig;
35391 * @scope Roo.BasicLayoutRegion
35395 * @event beforeremove
35396 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35397 * @param {Roo.LayoutRegion} this
35398 * @param {Roo.ContentPanel} panel The panel
35399 * @param {Object} e The cancel event object
35401 "beforeremove" : true,
35403 * @event invalidated
35404 * Fires when the layout for this region is changed.
35405 * @param {Roo.LayoutRegion} this
35407 "invalidated" : true,
35409 * @event visibilitychange
35410 * Fires when this region is shown or hidden
35411 * @param {Roo.LayoutRegion} this
35412 * @param {Boolean} visibility true or false
35414 "visibilitychange" : true,
35416 * @event paneladded
35417 * Fires when a panel is added.
35418 * @param {Roo.LayoutRegion} this
35419 * @param {Roo.ContentPanel} panel The panel
35421 "paneladded" : true,
35423 * @event panelremoved
35424 * Fires when a panel is removed.
35425 * @param {Roo.LayoutRegion} this
35426 * @param {Roo.ContentPanel} panel The panel
35428 "panelremoved" : true,
35430 * @event beforecollapse
35431 * Fires when this region before collapse.
35432 * @param {Roo.LayoutRegion} this
35434 "beforecollapse" : true,
35437 * Fires when this region is collapsed.
35438 * @param {Roo.LayoutRegion} this
35440 "collapsed" : true,
35443 * Fires when this region is expanded.
35444 * @param {Roo.LayoutRegion} this
35449 * Fires when this region is slid into view.
35450 * @param {Roo.LayoutRegion} this
35452 "slideshow" : true,
35455 * Fires when this region slides out of view.
35456 * @param {Roo.LayoutRegion} this
35458 "slidehide" : true,
35460 * @event panelactivated
35461 * Fires when a panel is activated.
35462 * @param {Roo.LayoutRegion} this
35463 * @param {Roo.ContentPanel} panel The activated panel
35465 "panelactivated" : true,
35468 * Fires when the user resizes this region.
35469 * @param {Roo.LayoutRegion} this
35470 * @param {Number} newSize The new size (width for east/west, height for north/south)
35474 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35475 this.panels = new Roo.util.MixedCollection();
35476 this.panels.getKey = this.getPanelId.createDelegate(this);
35478 this.activePanel = null;
35479 // ensure listeners are added...
35481 if (config.listeners || config.events) {
35482 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35483 listeners : config.listeners || {},
35484 events : config.events || {}
35488 if(skipConfig !== true){
35489 this.applyConfig(config);
35493 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35495 getPanelId : function(p){
35499 applyConfig : function(config){
35500 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35501 this.config = config;
35506 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35507 * the width, for horizontal (north, south) the height.
35508 * @param {Number} newSize The new width or height
35510 resizeTo : function(newSize){
35511 var el = this.el ? this.el :
35512 (this.activePanel ? this.activePanel.getEl() : null);
35514 switch(this.position){
35517 el.setWidth(newSize);
35518 this.fireEvent("resized", this, newSize);
35522 el.setHeight(newSize);
35523 this.fireEvent("resized", this, newSize);
35529 getBox : function(){
35530 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35533 getMargins : function(){
35534 return this.margins;
35537 updateBox : function(box){
35539 var el = this.activePanel.getEl();
35540 el.dom.style.left = box.x + "px";
35541 el.dom.style.top = box.y + "px";
35542 this.activePanel.setSize(box.width, box.height);
35546 * Returns the container element for this region.
35547 * @return {Roo.Element}
35549 getEl : function(){
35550 return this.activePanel;
35554 * Returns true if this region is currently visible.
35555 * @return {Boolean}
35557 isVisible : function(){
35558 return this.activePanel ? true : false;
35561 setActivePanel : function(panel){
35562 panel = this.getPanel(panel);
35563 if(this.activePanel && this.activePanel != panel){
35564 this.activePanel.setActiveState(false);
35565 this.activePanel.getEl().setLeftTop(-10000,-10000);
35567 this.activePanel = panel;
35568 panel.setActiveState(true);
35570 panel.setSize(this.box.width, this.box.height);
35572 this.fireEvent("panelactivated", this, panel);
35573 this.fireEvent("invalidated");
35577 * Show the specified panel.
35578 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35579 * @return {Roo.ContentPanel} The shown panel or null
35581 showPanel : function(panel){
35582 panel = this.getPanel(panel);
35584 this.setActivePanel(panel);
35590 * Get the active panel for this region.
35591 * @return {Roo.ContentPanel} The active panel or null
35593 getActivePanel : function(){
35594 return this.activePanel;
35598 * Add the passed ContentPanel(s)
35599 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35600 * @return {Roo.ContentPanel} The panel added (if only one was added)
35602 add : function(panel){
35603 if(arguments.length > 1){
35604 for(var i = 0, len = arguments.length; i < len; i++) {
35605 this.add(arguments[i]);
35609 if(this.hasPanel(panel)){
35610 this.showPanel(panel);
35613 var el = panel.getEl();
35614 if(el.dom.parentNode != this.mgr.el.dom){
35615 this.mgr.el.dom.appendChild(el.dom);
35617 if(panel.setRegion){
35618 panel.setRegion(this);
35620 this.panels.add(panel);
35621 el.setStyle("position", "absolute");
35622 if(!panel.background){
35623 this.setActivePanel(panel);
35624 if(this.config.initialSize && this.panels.getCount()==1){
35625 this.resizeTo(this.config.initialSize);
35628 this.fireEvent("paneladded", this, panel);
35633 * Returns true if the panel is in this region.
35634 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35635 * @return {Boolean}
35637 hasPanel : function(panel){
35638 if(typeof panel == "object"){ // must be panel obj
35639 panel = panel.getId();
35641 return this.getPanel(panel) ? true : false;
35645 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35646 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35647 * @param {Boolean} preservePanel Overrides the config preservePanel option
35648 * @return {Roo.ContentPanel} The panel that was removed
35650 remove : function(panel, preservePanel){
35651 panel = this.getPanel(panel);
35656 this.fireEvent("beforeremove", this, panel, e);
35657 if(e.cancel === true){
35660 var panelId = panel.getId();
35661 this.panels.removeKey(panelId);
35666 * Returns the panel specified or null if it's not in this region.
35667 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35668 * @return {Roo.ContentPanel}
35670 getPanel : function(id){
35671 if(typeof id == "object"){ // must be panel obj
35674 return this.panels.get(id);
35678 * Returns this regions position (north/south/east/west/center).
35681 getPosition: function(){
35682 return this.position;
35686 * Ext JS Library 1.1.1
35687 * Copyright(c) 2006-2007, Ext JS, LLC.
35689 * Originally Released Under LGPL - original licence link has changed is not relivant.
35692 * <script type="text/javascript">
35696 * @class Roo.bootstrap.layout.Region
35697 * @extends Roo.bootstrap.layout.Basic
35698 * This class represents a region in a layout manager.
35700 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35701 * @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})
35702 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35703 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35704 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35705 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35706 * @cfg {String} title The title for the region (overrides panel titles)
35707 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35708 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35709 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35710 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35711 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35712 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35713 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35714 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35715 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35716 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35718 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35719 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35720 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35721 * @cfg {Number} width For East/West panels
35722 * @cfg {Number} height For North/South panels
35723 * @cfg {Boolean} split To show the splitter
35724 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35726 * @cfg {string} cls Extra CSS classes to add to region
35728 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35729 * @cfg {string} region the region that it inhabits..
35732 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35733 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35735 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35736 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35737 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35739 Roo.bootstrap.layout.Region = function(config)
35741 this.applyConfig(config);
35743 var mgr = config.mgr;
35744 var pos = config.region;
35745 config.skipConfig = true;
35746 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35749 this.onRender(mgr.el);
35752 this.visible = true;
35753 this.collapsed = false;
35754 this.unrendered_panels = [];
35757 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35759 position: '', // set by wrapper (eg. north/south etc..)
35760 unrendered_panels : null, // unrendered panels.
35761 createBody : function(){
35762 /** This region's body element
35763 * @type Roo.Element */
35764 this.bodyEl = this.el.createChild({
35766 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35770 onRender: function(ctr, pos)
35772 var dh = Roo.DomHelper;
35773 /** This region's container element
35774 * @type Roo.Element */
35775 this.el = dh.append(ctr.dom, {
35777 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35779 /** This region's title element
35780 * @type Roo.Element */
35782 this.titleEl = dh.append(this.el.dom,
35785 unselectable: "on",
35786 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35788 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35789 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35792 this.titleEl.enableDisplayMode();
35793 /** This region's title text element
35794 * @type HTMLElement */
35795 this.titleTextEl = this.titleEl.dom.firstChild;
35796 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35798 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35799 this.closeBtn.enableDisplayMode();
35800 this.closeBtn.on("click", this.closeClicked, this);
35801 this.closeBtn.hide();
35803 this.createBody(this.config);
35804 if(this.config.hideWhenEmpty){
35806 this.on("paneladded", this.validateVisibility, this);
35807 this.on("panelremoved", this.validateVisibility, this);
35809 if(this.autoScroll){
35810 this.bodyEl.setStyle("overflow", "auto");
35812 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35814 //if(c.titlebar !== false){
35815 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35816 this.titleEl.hide();
35818 this.titleEl.show();
35819 if(this.config.title){
35820 this.titleTextEl.innerHTML = this.config.title;
35824 if(this.config.collapsed){
35825 this.collapse(true);
35827 if(this.config.hidden){
35831 if (this.unrendered_panels && this.unrendered_panels.length) {
35832 for (var i =0;i< this.unrendered_panels.length; i++) {
35833 this.add(this.unrendered_panels[i]);
35835 this.unrendered_panels = null;
35841 applyConfig : function(c)
35844 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35845 var dh = Roo.DomHelper;
35846 if(c.titlebar !== false){
35847 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35848 this.collapseBtn.on("click", this.collapse, this);
35849 this.collapseBtn.enableDisplayMode();
35851 if(c.showPin === true || this.showPin){
35852 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35853 this.stickBtn.enableDisplayMode();
35854 this.stickBtn.on("click", this.expand, this);
35855 this.stickBtn.hide();
35860 /** This region's collapsed element
35861 * @type Roo.Element */
35864 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35865 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35868 if(c.floatable !== false){
35869 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35870 this.collapsedEl.on("click", this.collapseClick, this);
35873 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35874 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35875 id: "message", unselectable: "on", style:{"float":"left"}});
35876 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35878 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35879 this.expandBtn.on("click", this.expand, this);
35883 if(this.collapseBtn){
35884 this.collapseBtn.setVisible(c.collapsible == true);
35887 this.cmargins = c.cmargins || this.cmargins ||
35888 (this.position == "west" || this.position == "east" ?
35889 {top: 0, left: 2, right:2, bottom: 0} :
35890 {top: 2, left: 0, right:0, bottom: 2});
35892 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35895 this.bottomTabs = c.tabPosition != "top";
35897 this.autoScroll = c.autoScroll || false;
35902 this.duration = c.duration || .30;
35903 this.slideDuration = c.slideDuration || .45;
35908 * Returns true if this region is currently visible.
35909 * @return {Boolean}
35911 isVisible : function(){
35912 return this.visible;
35916 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35917 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35919 //setCollapsedTitle : function(title){
35920 // title = title || " ";
35921 // if(this.collapsedTitleTextEl){
35922 // this.collapsedTitleTextEl.innerHTML = title;
35926 getBox : function(){
35928 // if(!this.collapsed){
35929 b = this.el.getBox(false, true);
35931 // b = this.collapsedEl.getBox(false, true);
35936 getMargins : function(){
35937 return this.margins;
35938 //return this.collapsed ? this.cmargins : this.margins;
35941 highlight : function(){
35942 this.el.addClass("x-layout-panel-dragover");
35945 unhighlight : function(){
35946 this.el.removeClass("x-layout-panel-dragover");
35949 updateBox : function(box)
35951 if (!this.bodyEl) {
35952 return; // not rendered yet..
35956 if(!this.collapsed){
35957 this.el.dom.style.left = box.x + "px";
35958 this.el.dom.style.top = box.y + "px";
35959 this.updateBody(box.width, box.height);
35961 this.collapsedEl.dom.style.left = box.x + "px";
35962 this.collapsedEl.dom.style.top = box.y + "px";
35963 this.collapsedEl.setSize(box.width, box.height);
35966 this.tabs.autoSizeTabs();
35970 updateBody : function(w, h)
35973 this.el.setWidth(w);
35974 w -= this.el.getBorderWidth("rl");
35975 if(this.config.adjustments){
35976 w += this.config.adjustments[0];
35979 if(h !== null && h > 0){
35980 this.el.setHeight(h);
35981 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35982 h -= this.el.getBorderWidth("tb");
35983 if(this.config.adjustments){
35984 h += this.config.adjustments[1];
35986 this.bodyEl.setHeight(h);
35988 h = this.tabs.syncHeight(h);
35991 if(this.panelSize){
35992 w = w !== null ? w : this.panelSize.width;
35993 h = h !== null ? h : this.panelSize.height;
35995 if(this.activePanel){
35996 var el = this.activePanel.getEl();
35997 w = w !== null ? w : el.getWidth();
35998 h = h !== null ? h : el.getHeight();
35999 this.panelSize = {width: w, height: h};
36000 this.activePanel.setSize(w, h);
36002 if(Roo.isIE && this.tabs){
36003 this.tabs.el.repaint();
36008 * Returns the container element for this region.
36009 * @return {Roo.Element}
36011 getEl : function(){
36016 * Hides this region.
36019 //if(!this.collapsed){
36020 this.el.dom.style.left = "-2000px";
36023 // this.collapsedEl.dom.style.left = "-2000px";
36024 // this.collapsedEl.hide();
36026 this.visible = false;
36027 this.fireEvent("visibilitychange", this, false);
36031 * Shows this region if it was previously hidden.
36034 //if(!this.collapsed){
36037 // this.collapsedEl.show();
36039 this.visible = true;
36040 this.fireEvent("visibilitychange", this, true);
36043 closeClicked : function(){
36044 if(this.activePanel){
36045 this.remove(this.activePanel);
36049 collapseClick : function(e){
36051 e.stopPropagation();
36054 e.stopPropagation();
36060 * Collapses this region.
36061 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36064 collapse : function(skipAnim, skipCheck = false){
36065 if(this.collapsed) {
36069 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36071 this.collapsed = true;
36073 this.split.el.hide();
36075 if(this.config.animate && skipAnim !== true){
36076 this.fireEvent("invalidated", this);
36077 this.animateCollapse();
36079 this.el.setLocation(-20000,-20000);
36081 this.collapsedEl.show();
36082 this.fireEvent("collapsed", this);
36083 this.fireEvent("invalidated", this);
36089 animateCollapse : function(){
36094 * Expands this region if it was previously collapsed.
36095 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36096 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36099 expand : function(e, skipAnim){
36101 e.stopPropagation();
36103 if(!this.collapsed || this.el.hasActiveFx()) {
36107 this.afterSlideIn();
36110 this.collapsed = false;
36111 if(this.config.animate && skipAnim !== true){
36112 this.animateExpand();
36116 this.split.el.show();
36118 this.collapsedEl.setLocation(-2000,-2000);
36119 this.collapsedEl.hide();
36120 this.fireEvent("invalidated", this);
36121 this.fireEvent("expanded", this);
36125 animateExpand : function(){
36129 initTabs : function()
36131 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36133 var ts = new Roo.bootstrap.panel.Tabs({
36134 el: this.bodyEl.dom,
36135 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36136 disableTooltips: this.config.disableTabTips,
36137 toolbar : this.config.toolbar
36140 if(this.config.hideTabs){
36141 ts.stripWrap.setDisplayed(false);
36144 ts.resizeTabs = this.config.resizeTabs === true;
36145 ts.minTabWidth = this.config.minTabWidth || 40;
36146 ts.maxTabWidth = this.config.maxTabWidth || 250;
36147 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36148 ts.monitorResize = false;
36149 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36150 ts.bodyEl.addClass('roo-layout-tabs-body');
36151 this.panels.each(this.initPanelAsTab, this);
36154 initPanelAsTab : function(panel){
36155 var ti = this.tabs.addTab(
36159 this.config.closeOnTab && panel.isClosable(),
36162 if(panel.tabTip !== undefined){
36163 ti.setTooltip(panel.tabTip);
36165 ti.on("activate", function(){
36166 this.setActivePanel(panel);
36169 if(this.config.closeOnTab){
36170 ti.on("beforeclose", function(t, e){
36172 this.remove(panel);
36176 panel.tabItem = ti;
36181 updatePanelTitle : function(panel, title)
36183 if(this.activePanel == panel){
36184 this.updateTitle(title);
36187 var ti = this.tabs.getTab(panel.getEl().id);
36189 if(panel.tabTip !== undefined){
36190 ti.setTooltip(panel.tabTip);
36195 updateTitle : function(title){
36196 if(this.titleTextEl && !this.config.title){
36197 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36201 setActivePanel : function(panel)
36203 panel = this.getPanel(panel);
36204 if(this.activePanel && this.activePanel != panel){
36205 if(this.activePanel.setActiveState(false) === false){
36209 this.activePanel = panel;
36210 panel.setActiveState(true);
36211 if(this.panelSize){
36212 panel.setSize(this.panelSize.width, this.panelSize.height);
36215 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36217 this.updateTitle(panel.getTitle());
36219 this.fireEvent("invalidated", this);
36221 this.fireEvent("panelactivated", this, panel);
36225 * Shows the specified panel.
36226 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36227 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36229 showPanel : function(panel)
36231 panel = this.getPanel(panel);
36234 var tab = this.tabs.getTab(panel.getEl().id);
36235 if(tab.isHidden()){
36236 this.tabs.unhideTab(tab.id);
36240 this.setActivePanel(panel);
36247 * Get the active panel for this region.
36248 * @return {Roo.ContentPanel} The active panel or null
36250 getActivePanel : function(){
36251 return this.activePanel;
36254 validateVisibility : function(){
36255 if(this.panels.getCount() < 1){
36256 this.updateTitle(" ");
36257 this.closeBtn.hide();
36260 if(!this.isVisible()){
36267 * Adds the passed ContentPanel(s) to this region.
36268 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36269 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36271 add : function(panel)
36273 if(arguments.length > 1){
36274 for(var i = 0, len = arguments.length; i < len; i++) {
36275 this.add(arguments[i]);
36280 // if we have not been rendered yet, then we can not really do much of this..
36281 if (!this.bodyEl) {
36282 this.unrendered_panels.push(panel);
36289 if(this.hasPanel(panel)){
36290 this.showPanel(panel);
36293 panel.setRegion(this);
36294 this.panels.add(panel);
36295 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36296 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36297 // and hide them... ???
36298 this.bodyEl.dom.appendChild(panel.getEl().dom);
36299 if(panel.background !== true){
36300 this.setActivePanel(panel);
36302 this.fireEvent("paneladded", this, panel);
36309 this.initPanelAsTab(panel);
36313 if(panel.background !== true){
36314 this.tabs.activate(panel.getEl().id);
36316 this.fireEvent("paneladded", this, panel);
36321 * Hides the tab for the specified panel.
36322 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36324 hidePanel : function(panel){
36325 if(this.tabs && (panel = this.getPanel(panel))){
36326 this.tabs.hideTab(panel.getEl().id);
36331 * Unhides the tab for a previously hidden panel.
36332 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36334 unhidePanel : function(panel){
36335 if(this.tabs && (panel = this.getPanel(panel))){
36336 this.tabs.unhideTab(panel.getEl().id);
36340 clearPanels : function(){
36341 while(this.panels.getCount() > 0){
36342 this.remove(this.panels.first());
36347 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36348 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36349 * @param {Boolean} preservePanel Overrides the config preservePanel option
36350 * @return {Roo.ContentPanel} The panel that was removed
36352 remove : function(panel, preservePanel)
36354 panel = this.getPanel(panel);
36359 this.fireEvent("beforeremove", this, panel, e);
36360 if(e.cancel === true){
36363 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36364 var panelId = panel.getId();
36365 this.panels.removeKey(panelId);
36367 document.body.appendChild(panel.getEl().dom);
36370 this.tabs.removeTab(panel.getEl().id);
36371 }else if (!preservePanel){
36372 this.bodyEl.dom.removeChild(panel.getEl().dom);
36374 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36375 var p = this.panels.first();
36376 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36377 tempEl.appendChild(p.getEl().dom);
36378 this.bodyEl.update("");
36379 this.bodyEl.dom.appendChild(p.getEl().dom);
36381 this.updateTitle(p.getTitle());
36383 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36384 this.setActivePanel(p);
36386 panel.setRegion(null);
36387 if(this.activePanel == panel){
36388 this.activePanel = null;
36390 if(this.config.autoDestroy !== false && preservePanel !== true){
36391 try{panel.destroy();}catch(e){}
36393 this.fireEvent("panelremoved", this, panel);
36398 * Returns the TabPanel component used by this region
36399 * @return {Roo.TabPanel}
36401 getTabs : function(){
36405 createTool : function(parentEl, className){
36406 var btn = Roo.DomHelper.append(parentEl, {
36408 cls: "x-layout-tools-button",
36411 cls: "roo-layout-tools-button-inner " + className,
36415 btn.addClassOnOver("roo-layout-tools-button-over");
36420 * Ext JS Library 1.1.1
36421 * Copyright(c) 2006-2007, Ext JS, LLC.
36423 * Originally Released Under LGPL - original licence link has changed is not relivant.
36426 * <script type="text/javascript">
36432 * @class Roo.SplitLayoutRegion
36433 * @extends Roo.LayoutRegion
36434 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36436 Roo.bootstrap.layout.Split = function(config){
36437 this.cursor = config.cursor;
36438 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36441 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36443 splitTip : "Drag to resize.",
36444 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36445 useSplitTips : false,
36447 applyConfig : function(config){
36448 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36451 onRender : function(ctr,pos) {
36453 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36454 if(!this.config.split){
36459 var splitEl = Roo.DomHelper.append(ctr.dom, {
36461 id: this.el.id + "-split",
36462 cls: "roo-layout-split roo-layout-split-"+this.position,
36465 /** The SplitBar for this region
36466 * @type Roo.SplitBar */
36467 // does not exist yet...
36468 Roo.log([this.position, this.orientation]);
36470 this.split = new Roo.bootstrap.SplitBar({
36471 dragElement : splitEl,
36472 resizingElement: this.el,
36473 orientation : this.orientation
36476 this.split.on("moved", this.onSplitMove, this);
36477 this.split.useShim = this.config.useShim === true;
36478 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36479 if(this.useSplitTips){
36480 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36482 //if(config.collapsible){
36483 // this.split.el.on("dblclick", this.collapse, this);
36486 if(typeof this.config.minSize != "undefined"){
36487 this.split.minSize = this.config.minSize;
36489 if(typeof this.config.maxSize != "undefined"){
36490 this.split.maxSize = this.config.maxSize;
36492 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36493 this.hideSplitter();
36498 getHMaxSize : function(){
36499 var cmax = this.config.maxSize || 10000;
36500 var center = this.mgr.getRegion("center");
36501 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36504 getVMaxSize : function(){
36505 var cmax = this.config.maxSize || 10000;
36506 var center = this.mgr.getRegion("center");
36507 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36510 onSplitMove : function(split, newSize){
36511 this.fireEvent("resized", this, newSize);
36515 * Returns the {@link Roo.SplitBar} for this region.
36516 * @return {Roo.SplitBar}
36518 getSplitBar : function(){
36523 this.hideSplitter();
36524 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36527 hideSplitter : function(){
36529 this.split.el.setLocation(-2000,-2000);
36530 this.split.el.hide();
36536 this.split.el.show();
36538 Roo.bootstrap.layout.Split.superclass.show.call(this);
36541 beforeSlide: function(){
36542 if(Roo.isGecko){// firefox overflow auto bug workaround
36543 this.bodyEl.clip();
36545 this.tabs.bodyEl.clip();
36547 if(this.activePanel){
36548 this.activePanel.getEl().clip();
36550 if(this.activePanel.beforeSlide){
36551 this.activePanel.beforeSlide();
36557 afterSlide : function(){
36558 if(Roo.isGecko){// firefox overflow auto bug workaround
36559 this.bodyEl.unclip();
36561 this.tabs.bodyEl.unclip();
36563 if(this.activePanel){
36564 this.activePanel.getEl().unclip();
36565 if(this.activePanel.afterSlide){
36566 this.activePanel.afterSlide();
36572 initAutoHide : function(){
36573 if(this.autoHide !== false){
36574 if(!this.autoHideHd){
36575 var st = new Roo.util.DelayedTask(this.slideIn, this);
36576 this.autoHideHd = {
36577 "mouseout": function(e){
36578 if(!e.within(this.el, true)){
36582 "mouseover" : function(e){
36588 this.el.on(this.autoHideHd);
36592 clearAutoHide : function(){
36593 if(this.autoHide !== false){
36594 this.el.un("mouseout", this.autoHideHd.mouseout);
36595 this.el.un("mouseover", this.autoHideHd.mouseover);
36599 clearMonitor : function(){
36600 Roo.get(document).un("click", this.slideInIf, this);
36603 // these names are backwards but not changed for compat
36604 slideOut : function(){
36605 if(this.isSlid || this.el.hasActiveFx()){
36608 this.isSlid = true;
36609 if(this.collapseBtn){
36610 this.collapseBtn.hide();
36612 this.closeBtnState = this.closeBtn.getStyle('display');
36613 this.closeBtn.hide();
36615 this.stickBtn.show();
36618 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36619 this.beforeSlide();
36620 this.el.setStyle("z-index", 10001);
36621 this.el.slideIn(this.getSlideAnchor(), {
36622 callback: function(){
36624 this.initAutoHide();
36625 Roo.get(document).on("click", this.slideInIf, this);
36626 this.fireEvent("slideshow", this);
36633 afterSlideIn : function(){
36634 this.clearAutoHide();
36635 this.isSlid = false;
36636 this.clearMonitor();
36637 this.el.setStyle("z-index", "");
36638 if(this.collapseBtn){
36639 this.collapseBtn.show();
36641 this.closeBtn.setStyle('display', this.closeBtnState);
36643 this.stickBtn.hide();
36645 this.fireEvent("slidehide", this);
36648 slideIn : function(cb){
36649 if(!this.isSlid || this.el.hasActiveFx()){
36653 this.isSlid = false;
36654 this.beforeSlide();
36655 this.el.slideOut(this.getSlideAnchor(), {
36656 callback: function(){
36657 this.el.setLeftTop(-10000, -10000);
36659 this.afterSlideIn();
36667 slideInIf : function(e){
36668 if(!e.within(this.el)){
36673 animateCollapse : function(){
36674 this.beforeSlide();
36675 this.el.setStyle("z-index", 20000);
36676 var anchor = this.getSlideAnchor();
36677 this.el.slideOut(anchor, {
36678 callback : function(){
36679 this.el.setStyle("z-index", "");
36680 this.collapsedEl.slideIn(anchor, {duration:.3});
36682 this.el.setLocation(-10000,-10000);
36684 this.fireEvent("collapsed", this);
36691 animateExpand : function(){
36692 this.beforeSlide();
36693 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36694 this.el.setStyle("z-index", 20000);
36695 this.collapsedEl.hide({
36698 this.el.slideIn(this.getSlideAnchor(), {
36699 callback : function(){
36700 this.el.setStyle("z-index", "");
36703 this.split.el.show();
36705 this.fireEvent("invalidated", this);
36706 this.fireEvent("expanded", this);
36734 getAnchor : function(){
36735 return this.anchors[this.position];
36738 getCollapseAnchor : function(){
36739 return this.canchors[this.position];
36742 getSlideAnchor : function(){
36743 return this.sanchors[this.position];
36746 getAlignAdj : function(){
36747 var cm = this.cmargins;
36748 switch(this.position){
36764 getExpandAdj : function(){
36765 var c = this.collapsedEl, cm = this.cmargins;
36766 switch(this.position){
36768 return [-(cm.right+c.getWidth()+cm.left), 0];
36771 return [cm.right+c.getWidth()+cm.left, 0];
36774 return [0, -(cm.top+cm.bottom+c.getHeight())];
36777 return [0, cm.top+cm.bottom+c.getHeight()];
36783 * Ext JS Library 1.1.1
36784 * Copyright(c) 2006-2007, Ext JS, LLC.
36786 * Originally Released Under LGPL - original licence link has changed is not relivant.
36789 * <script type="text/javascript">
36792 * These classes are private internal classes
36794 Roo.bootstrap.layout.Center = function(config){
36795 config.region = "center";
36796 Roo.bootstrap.layout.Region.call(this, config);
36797 this.visible = true;
36798 this.minWidth = config.minWidth || 20;
36799 this.minHeight = config.minHeight || 20;
36802 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36804 // center panel can't be hidden
36808 // center panel can't be hidden
36811 getMinWidth: function(){
36812 return this.minWidth;
36815 getMinHeight: function(){
36816 return this.minHeight;
36829 Roo.bootstrap.layout.North = function(config)
36831 config.region = 'north';
36832 config.cursor = 'n-resize';
36834 Roo.bootstrap.layout.Split.call(this, config);
36838 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36839 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36840 this.split.el.addClass("roo-layout-split-v");
36842 var size = config.initialSize || config.height;
36843 if(typeof size != "undefined"){
36844 this.el.setHeight(size);
36847 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36849 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36853 getBox : function(){
36854 if(this.collapsed){
36855 return this.collapsedEl.getBox();
36857 var box = this.el.getBox();
36859 box.height += this.split.el.getHeight();
36864 updateBox : function(box){
36865 if(this.split && !this.collapsed){
36866 box.height -= this.split.el.getHeight();
36867 this.split.el.setLeft(box.x);
36868 this.split.el.setTop(box.y+box.height);
36869 this.split.el.setWidth(box.width);
36871 if(this.collapsed){
36872 this.updateBody(box.width, null);
36874 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36882 Roo.bootstrap.layout.South = function(config){
36883 config.region = 'south';
36884 config.cursor = 's-resize';
36885 Roo.bootstrap.layout.Split.call(this, config);
36887 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36888 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36889 this.split.el.addClass("roo-layout-split-v");
36891 var size = config.initialSize || config.height;
36892 if(typeof size != "undefined"){
36893 this.el.setHeight(size);
36897 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36898 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36899 getBox : function(){
36900 if(this.collapsed){
36901 return this.collapsedEl.getBox();
36903 var box = this.el.getBox();
36905 var sh = this.split.el.getHeight();
36912 updateBox : function(box){
36913 if(this.split && !this.collapsed){
36914 var sh = this.split.el.getHeight();
36917 this.split.el.setLeft(box.x);
36918 this.split.el.setTop(box.y-sh);
36919 this.split.el.setWidth(box.width);
36921 if(this.collapsed){
36922 this.updateBody(box.width, null);
36924 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36928 Roo.bootstrap.layout.East = function(config){
36929 config.region = "east";
36930 config.cursor = "e-resize";
36931 Roo.bootstrap.layout.Split.call(this, config);
36933 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36934 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36935 this.split.el.addClass("roo-layout-split-h");
36937 var size = config.initialSize || config.width;
36938 if(typeof size != "undefined"){
36939 this.el.setWidth(size);
36942 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36943 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36944 getBox : function(){
36945 if(this.collapsed){
36946 return this.collapsedEl.getBox();
36948 var box = this.el.getBox();
36950 var sw = this.split.el.getWidth();
36957 updateBox : function(box){
36958 if(this.split && !this.collapsed){
36959 var sw = this.split.el.getWidth();
36961 this.split.el.setLeft(box.x);
36962 this.split.el.setTop(box.y);
36963 this.split.el.setHeight(box.height);
36966 if(this.collapsed){
36967 this.updateBody(null, box.height);
36969 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36973 Roo.bootstrap.layout.West = function(config){
36974 config.region = "west";
36975 config.cursor = "w-resize";
36977 Roo.bootstrap.layout.Split.call(this, config);
36979 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36980 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36981 this.split.el.addClass("roo-layout-split-h");
36985 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36986 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36988 onRender: function(ctr, pos)
36990 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36991 var size = this.config.initialSize || this.config.width;
36992 if(typeof size != "undefined"){
36993 this.el.setWidth(size);
36997 getBox : function(){
36998 if(this.collapsed){
36999 return this.collapsedEl.getBox();
37001 var box = this.el.getBox();
37003 box.width += this.split.el.getWidth();
37008 updateBox : function(box){
37009 if(this.split && !this.collapsed){
37010 var sw = this.split.el.getWidth();
37012 this.split.el.setLeft(box.x+box.width);
37013 this.split.el.setTop(box.y);
37014 this.split.el.setHeight(box.height);
37016 if(this.collapsed){
37017 this.updateBody(null, box.height);
37019 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37022 Roo.namespace("Roo.bootstrap.panel");/*
37024 * Ext JS Library 1.1.1
37025 * Copyright(c) 2006-2007, Ext JS, LLC.
37027 * Originally Released Under LGPL - original licence link has changed is not relivant.
37030 * <script type="text/javascript">
37033 * @class Roo.ContentPanel
37034 * @extends Roo.util.Observable
37035 * A basic ContentPanel element.
37036 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37037 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37038 * @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
37039 * @cfg {Boolean} closable True if the panel can be closed/removed
37040 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37041 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37042 * @cfg {Toolbar} toolbar A toolbar for this panel
37043 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37044 * @cfg {String} title The title for this panel
37045 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37046 * @cfg {String} url Calls {@link #setUrl} with this value
37047 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37048 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37049 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37050 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37051 * @cfg {Boolean} badges render the badges
37054 * Create a new ContentPanel.
37055 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37056 * @param {String/Object} config A string to set only the title or a config object
37057 * @param {String} content (optional) Set the HTML content for this panel
37058 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37060 Roo.bootstrap.panel.Content = function( config){
37062 this.tpl = config.tpl || false;
37064 var el = config.el;
37065 var content = config.content;
37067 if(config.autoCreate){ // xtype is available if this is called from factory
37070 this.el = Roo.get(el);
37071 if(!this.el && config && config.autoCreate){
37072 if(typeof config.autoCreate == "object"){
37073 if(!config.autoCreate.id){
37074 config.autoCreate.id = config.id||el;
37076 this.el = Roo.DomHelper.append(document.body,
37077 config.autoCreate, true);
37079 var elcfg = { tag: "div",
37080 cls: "roo-layout-inactive-content",
37084 elcfg.html = config.html;
37088 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37091 this.closable = false;
37092 this.loaded = false;
37093 this.active = false;
37096 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37098 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37100 this.wrapEl = this.el; //this.el.wrap();
37102 if (config.toolbar.items) {
37103 ti = config.toolbar.items ;
37104 delete config.toolbar.items ;
37108 this.toolbar.render(this.wrapEl, 'before');
37109 for(var i =0;i < ti.length;i++) {
37110 // Roo.log(['add child', items[i]]);
37111 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37113 this.toolbar.items = nitems;
37114 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37115 delete config.toolbar;
37119 // xtype created footer. - not sure if will work as we normally have to render first..
37120 if (this.footer && !this.footer.el && this.footer.xtype) {
37121 if (!this.wrapEl) {
37122 this.wrapEl = this.el.wrap();
37125 this.footer.container = this.wrapEl.createChild();
37127 this.footer = Roo.factory(this.footer, Roo);
37132 if(typeof config == "string"){
37133 this.title = config;
37135 Roo.apply(this, config);
37139 this.resizeEl = Roo.get(this.resizeEl, true);
37141 this.resizeEl = this.el;
37143 // handle view.xtype
37151 * Fires when this panel is activated.
37152 * @param {Roo.ContentPanel} this
37156 * @event deactivate
37157 * Fires when this panel is activated.
37158 * @param {Roo.ContentPanel} this
37160 "deactivate" : true,
37164 * Fires when this panel is resized if fitToFrame is true.
37165 * @param {Roo.ContentPanel} this
37166 * @param {Number} width The width after any component adjustments
37167 * @param {Number} height The height after any component adjustments
37173 * Fires when this tab is created
37174 * @param {Roo.ContentPanel} this
37185 if(this.autoScroll){
37186 this.resizeEl.setStyle("overflow", "auto");
37188 // fix randome scrolling
37189 //this.el.on('scroll', function() {
37190 // Roo.log('fix random scolling');
37191 // this.scrollTo('top',0);
37194 content = content || this.content;
37196 this.setContent(content);
37198 if(config && config.url){
37199 this.setUrl(this.url, this.params, this.loadOnce);
37204 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37206 if (this.view && typeof(this.view.xtype) != 'undefined') {
37207 this.view.el = this.el.appendChild(document.createElement("div"));
37208 this.view = Roo.factory(this.view);
37209 this.view.render && this.view.render(false, '');
37213 this.fireEvent('render', this);
37216 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37220 setRegion : function(region){
37221 this.region = region;
37222 this.setActiveClass(region && !this.background);
37226 setActiveClass: function(state)
37229 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37230 this.el.setStyle('position','relative');
37232 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37233 this.el.setStyle('position', 'absolute');
37238 * Returns the toolbar for this Panel if one was configured.
37239 * @return {Roo.Toolbar}
37241 getToolbar : function(){
37242 return this.toolbar;
37245 setActiveState : function(active)
37247 this.active = active;
37248 this.setActiveClass(active);
37250 if(this.fireEvent("deactivate", this) === false){
37255 this.fireEvent("activate", this);
37259 * Updates this panel's element
37260 * @param {String} content The new content
37261 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37263 setContent : function(content, loadScripts){
37264 this.el.update(content, loadScripts);
37267 ignoreResize : function(w, h){
37268 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37271 this.lastSize = {width: w, height: h};
37276 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37277 * @return {Roo.UpdateManager} The UpdateManager
37279 getUpdateManager : function(){
37280 return this.el.getUpdateManager();
37283 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37284 * @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:
37287 url: "your-url.php",
37288 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37289 callback: yourFunction,
37290 scope: yourObject, //(optional scope)
37293 text: "Loading...",
37298 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37299 * 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.
37300 * @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}
37301 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37302 * @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.
37303 * @return {Roo.ContentPanel} this
37306 var um = this.el.getUpdateManager();
37307 um.update.apply(um, arguments);
37313 * 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.
37314 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37315 * @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)
37316 * @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)
37317 * @return {Roo.UpdateManager} The UpdateManager
37319 setUrl : function(url, params, loadOnce){
37320 if(this.refreshDelegate){
37321 this.removeListener("activate", this.refreshDelegate);
37323 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37324 this.on("activate", this.refreshDelegate);
37325 return this.el.getUpdateManager();
37328 _handleRefresh : function(url, params, loadOnce){
37329 if(!loadOnce || !this.loaded){
37330 var updater = this.el.getUpdateManager();
37331 updater.update(url, params, this._setLoaded.createDelegate(this));
37335 _setLoaded : function(){
37336 this.loaded = true;
37340 * Returns this panel's id
37343 getId : function(){
37348 * Returns this panel's element - used by regiosn to add.
37349 * @return {Roo.Element}
37351 getEl : function(){
37352 return this.wrapEl || this.el;
37357 adjustForComponents : function(width, height)
37359 //Roo.log('adjustForComponents ');
37360 if(this.resizeEl != this.el){
37361 width -= this.el.getFrameWidth('lr');
37362 height -= this.el.getFrameWidth('tb');
37365 var te = this.toolbar.getEl();
37366 te.setWidth(width);
37367 height -= te.getHeight();
37370 var te = this.footer.getEl();
37371 te.setWidth(width);
37372 height -= te.getHeight();
37376 if(this.adjustments){
37377 width += this.adjustments[0];
37378 height += this.adjustments[1];
37380 return {"width": width, "height": height};
37383 setSize : function(width, height){
37384 if(this.fitToFrame && !this.ignoreResize(width, height)){
37385 if(this.fitContainer && this.resizeEl != this.el){
37386 this.el.setSize(width, height);
37388 var size = this.adjustForComponents(width, height);
37389 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37390 this.fireEvent('resize', this, size.width, size.height);
37395 * Returns this panel's title
37398 getTitle : function(){
37400 if (typeof(this.title) != 'object') {
37405 for (var k in this.title) {
37406 if (!this.title.hasOwnProperty(k)) {
37410 if (k.indexOf('-') >= 0) {
37411 var s = k.split('-');
37412 for (var i = 0; i<s.length; i++) {
37413 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37416 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37423 * Set this panel's title
37424 * @param {String} title
37426 setTitle : function(title){
37427 this.title = title;
37429 this.region.updatePanelTitle(this, title);
37434 * Returns true is this panel was configured to be closable
37435 * @return {Boolean}
37437 isClosable : function(){
37438 return this.closable;
37441 beforeSlide : function(){
37443 this.resizeEl.clip();
37446 afterSlide : function(){
37448 this.resizeEl.unclip();
37452 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37453 * Will fail silently if the {@link #setUrl} method has not been called.
37454 * This does not activate the panel, just updates its content.
37456 refresh : function(){
37457 if(this.refreshDelegate){
37458 this.loaded = false;
37459 this.refreshDelegate();
37464 * Destroys this panel
37466 destroy : function(){
37467 this.el.removeAllListeners();
37468 var tempEl = document.createElement("span");
37469 tempEl.appendChild(this.el.dom);
37470 tempEl.innerHTML = "";
37476 * form - if the content panel contains a form - this is a reference to it.
37477 * @type {Roo.form.Form}
37481 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37482 * This contains a reference to it.
37488 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37498 * @param {Object} cfg Xtype definition of item to add.
37502 getChildContainer: function () {
37503 return this.getEl();
37508 var ret = new Roo.factory(cfg);
37513 if (cfg.xtype.match(/^Form$/)) {
37516 //if (this.footer) {
37517 // el = this.footer.container.insertSibling(false, 'before');
37519 el = this.el.createChild();
37522 this.form = new Roo.form.Form(cfg);
37525 if ( this.form.allItems.length) {
37526 this.form.render(el.dom);
37530 // should only have one of theses..
37531 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37532 // views.. should not be just added - used named prop 'view''
37534 cfg.el = this.el.appendChild(document.createElement("div"));
37537 var ret = new Roo.factory(cfg);
37539 ret.render && ret.render(false, ''); // render blank..
37549 * @class Roo.bootstrap.panel.Grid
37550 * @extends Roo.bootstrap.panel.Content
37552 * Create a new GridPanel.
37553 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37554 * @param {Object} config A the config object
37560 Roo.bootstrap.panel.Grid = function(config)
37564 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37565 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37567 config.el = this.wrapper;
37568 //this.el = this.wrapper;
37570 if (config.container) {
37571 // ctor'ed from a Border/panel.grid
37574 this.wrapper.setStyle("overflow", "hidden");
37575 this.wrapper.addClass('roo-grid-container');
37580 if(config.toolbar){
37581 var tool_el = this.wrapper.createChild();
37582 this.toolbar = Roo.factory(config.toolbar);
37584 if (config.toolbar.items) {
37585 ti = config.toolbar.items ;
37586 delete config.toolbar.items ;
37590 this.toolbar.render(tool_el);
37591 for(var i =0;i < ti.length;i++) {
37592 // Roo.log(['add child', items[i]]);
37593 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37595 this.toolbar.items = nitems;
37597 delete config.toolbar;
37600 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37601 config.grid.scrollBody = true;;
37602 config.grid.monitorWindowResize = false; // turn off autosizing
37603 config.grid.autoHeight = false;
37604 config.grid.autoWidth = false;
37606 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37608 if (config.background) {
37609 // render grid on panel activation (if panel background)
37610 this.on('activate', function(gp) {
37611 if (!gp.grid.rendered) {
37612 gp.grid.render(this.wrapper);
37613 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37618 this.grid.render(this.wrapper);
37619 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37622 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37623 // ??? needed ??? config.el = this.wrapper;
37628 // xtype created footer. - not sure if will work as we normally have to render first..
37629 if (this.footer && !this.footer.el && this.footer.xtype) {
37631 var ctr = this.grid.getView().getFooterPanel(true);
37632 this.footer.dataSource = this.grid.dataSource;
37633 this.footer = Roo.factory(this.footer, Roo);
37634 this.footer.render(ctr);
37644 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37645 getId : function(){
37646 return this.grid.id;
37650 * Returns the grid for this panel
37651 * @return {Roo.bootstrap.Table}
37653 getGrid : function(){
37657 setSize : function(width, height){
37658 if(!this.ignoreResize(width, height)){
37659 var grid = this.grid;
37660 var size = this.adjustForComponents(width, height);
37661 var gridel = grid.getGridEl();
37662 gridel.setSize(size.width, size.height);
37664 var thd = grid.getGridEl().select('thead',true).first();
37665 var tbd = grid.getGridEl().select('tbody', true).first();
37667 tbd.setSize(width, height - thd.getHeight());
37676 beforeSlide : function(){
37677 this.grid.getView().scroller.clip();
37680 afterSlide : function(){
37681 this.grid.getView().scroller.unclip();
37684 destroy : function(){
37685 this.grid.destroy();
37687 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37692 * @class Roo.bootstrap.panel.Nest
37693 * @extends Roo.bootstrap.panel.Content
37695 * Create a new Panel, that can contain a layout.Border.
37698 * @param {Roo.BorderLayout} layout The layout for this panel
37699 * @param {String/Object} config A string to set only the title or a config object
37701 Roo.bootstrap.panel.Nest = function(config)
37703 // construct with only one argument..
37704 /* FIXME - implement nicer consturctors
37705 if (layout.layout) {
37707 layout = config.layout;
37708 delete config.layout;
37710 if (layout.xtype && !layout.getEl) {
37711 // then layout needs constructing..
37712 layout = Roo.factory(layout, Roo);
37716 config.el = config.layout.getEl();
37718 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37720 config.layout.monitorWindowResize = false; // turn off autosizing
37721 this.layout = config.layout;
37722 this.layout.getEl().addClass("roo-layout-nested-layout");
37729 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37731 setSize : function(width, height){
37732 if(!this.ignoreResize(width, height)){
37733 var size = this.adjustForComponents(width, height);
37734 var el = this.layout.getEl();
37735 if (size.height < 1) {
37736 el.setWidth(size.width);
37738 el.setSize(size.width, size.height);
37740 var touch = el.dom.offsetWidth;
37741 this.layout.layout();
37742 // ie requires a double layout on the first pass
37743 if(Roo.isIE && !this.initialized){
37744 this.initialized = true;
37745 this.layout.layout();
37750 // activate all subpanels if not currently active..
37752 setActiveState : function(active){
37753 this.active = active;
37754 this.setActiveClass(active);
37757 this.fireEvent("deactivate", this);
37761 this.fireEvent("activate", this);
37762 // not sure if this should happen before or after..
37763 if (!this.layout) {
37764 return; // should not happen..
37767 for (var r in this.layout.regions) {
37768 reg = this.layout.getRegion(r);
37769 if (reg.getActivePanel()) {
37770 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37771 reg.setActivePanel(reg.getActivePanel());
37774 if (!reg.panels.length) {
37777 reg.showPanel(reg.getPanel(0));
37786 * Returns the nested BorderLayout for this panel
37787 * @return {Roo.BorderLayout}
37789 getLayout : function(){
37790 return this.layout;
37794 * Adds a xtype elements to the layout of the nested panel
37798 xtype : 'ContentPanel',
37805 xtype : 'NestedLayoutPanel',
37811 items : [ ... list of content panels or nested layout panels.. ]
37815 * @param {Object} cfg Xtype definition of item to add.
37817 addxtype : function(cfg) {
37818 return this.layout.addxtype(cfg);
37823 * Ext JS Library 1.1.1
37824 * Copyright(c) 2006-2007, Ext JS, LLC.
37826 * Originally Released Under LGPL - original licence link has changed is not relivant.
37829 * <script type="text/javascript">
37832 * @class Roo.TabPanel
37833 * @extends Roo.util.Observable
37834 * A lightweight tab container.
37838 // basic tabs 1, built from existing content
37839 var tabs = new Roo.TabPanel("tabs1");
37840 tabs.addTab("script", "View Script");
37841 tabs.addTab("markup", "View Markup");
37842 tabs.activate("script");
37844 // more advanced tabs, built from javascript
37845 var jtabs = new Roo.TabPanel("jtabs");
37846 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37848 // set up the UpdateManager
37849 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37850 var updater = tab2.getUpdateManager();
37851 updater.setDefaultUrl("ajax1.htm");
37852 tab2.on('activate', updater.refresh, updater, true);
37854 // Use setUrl for Ajax loading
37855 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37856 tab3.setUrl("ajax2.htm", null, true);
37859 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37862 jtabs.activate("jtabs-1");
37865 * Create a new TabPanel.
37866 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37867 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37869 Roo.bootstrap.panel.Tabs = function(config){
37871 * The container element for this TabPanel.
37872 * @type Roo.Element
37874 this.el = Roo.get(config.el);
37877 if(typeof config == "boolean"){
37878 this.tabPosition = config ? "bottom" : "top";
37880 Roo.apply(this, config);
37884 if(this.tabPosition == "bottom"){
37885 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37886 this.el.addClass("roo-tabs-bottom");
37888 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37889 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37890 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37892 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37894 if(this.tabPosition != "bottom"){
37895 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37896 * @type Roo.Element
37898 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37899 this.el.addClass("roo-tabs-top");
37903 this.bodyEl.setStyle("position", "relative");
37905 this.active = null;
37906 this.activateDelegate = this.activate.createDelegate(this);
37911 * Fires when the active tab changes
37912 * @param {Roo.TabPanel} this
37913 * @param {Roo.TabPanelItem} activePanel The new active tab
37917 * @event beforetabchange
37918 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37919 * @param {Roo.TabPanel} this
37920 * @param {Object} e Set cancel to true on this object to cancel the tab change
37921 * @param {Roo.TabPanelItem} tab The tab being changed to
37923 "beforetabchange" : true
37926 Roo.EventManager.onWindowResize(this.onResize, this);
37927 this.cpad = this.el.getPadding("lr");
37928 this.hiddenCount = 0;
37931 // toolbar on the tabbar support...
37932 if (this.toolbar) {
37933 alert("no toolbar support yet");
37934 this.toolbar = false;
37936 var tcfg = this.toolbar;
37937 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37938 this.toolbar = new Roo.Toolbar(tcfg);
37939 if (Roo.isSafari) {
37940 var tbl = tcfg.container.child('table', true);
37941 tbl.setAttribute('width', '100%');
37949 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37952 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37954 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37956 tabPosition : "top",
37958 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37960 currentTabWidth : 0,
37962 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37966 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37970 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37972 preferredTabWidth : 175,
37974 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37976 resizeTabs : false,
37978 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37980 monitorResize : true,
37982 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37987 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37988 * @param {String} id The id of the div to use <b>or create</b>
37989 * @param {String} text The text for the tab
37990 * @param {String} content (optional) Content to put in the TabPanelItem body
37991 * @param {Boolean} closable (optional) True to create a close icon on the tab
37992 * @return {Roo.TabPanelItem} The created TabPanelItem
37994 addTab : function(id, text, content, closable, tpl)
37996 var item = new Roo.bootstrap.panel.TabItem({
38000 closable : closable,
38003 this.addTabItem(item);
38005 item.setContent(content);
38011 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38012 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38013 * @return {Roo.TabPanelItem}
38015 getTab : function(id){
38016 return this.items[id];
38020 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38021 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38023 hideTab : function(id){
38024 var t = this.items[id];
38027 this.hiddenCount++;
38028 this.autoSizeTabs();
38033 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38034 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38036 unhideTab : function(id){
38037 var t = this.items[id];
38039 t.setHidden(false);
38040 this.hiddenCount--;
38041 this.autoSizeTabs();
38046 * Adds an existing {@link Roo.TabPanelItem}.
38047 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38049 addTabItem : function(item){
38050 this.items[item.id] = item;
38051 this.items.push(item);
38052 // if(this.resizeTabs){
38053 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38054 // this.autoSizeTabs();
38056 // item.autoSize();
38061 * Removes a {@link Roo.TabPanelItem}.
38062 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38064 removeTab : function(id){
38065 var items = this.items;
38066 var tab = items[id];
38067 if(!tab) { return; }
38068 var index = items.indexOf(tab);
38069 if(this.active == tab && items.length > 1){
38070 var newTab = this.getNextAvailable(index);
38075 this.stripEl.dom.removeChild(tab.pnode.dom);
38076 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38077 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38079 items.splice(index, 1);
38080 delete this.items[tab.id];
38081 tab.fireEvent("close", tab);
38082 tab.purgeListeners();
38083 this.autoSizeTabs();
38086 getNextAvailable : function(start){
38087 var items = this.items;
38089 // look for a next tab that will slide over to
38090 // replace the one being removed
38091 while(index < items.length){
38092 var item = items[++index];
38093 if(item && !item.isHidden()){
38097 // if one isn't found select the previous tab (on the left)
38100 var item = items[--index];
38101 if(item && !item.isHidden()){
38109 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38110 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38112 disableTab : function(id){
38113 var tab = this.items[id];
38114 if(tab && this.active != tab){
38120 * Enables a {@link Roo.TabPanelItem} that is disabled.
38121 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38123 enableTab : function(id){
38124 var tab = this.items[id];
38129 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38130 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38131 * @return {Roo.TabPanelItem} The TabPanelItem.
38133 activate : function(id){
38134 var tab = this.items[id];
38138 if(tab == this.active || tab.disabled){
38142 this.fireEvent("beforetabchange", this, e, tab);
38143 if(e.cancel !== true && !tab.disabled){
38145 this.active.hide();
38147 this.active = this.items[id];
38148 this.active.show();
38149 this.fireEvent("tabchange", this, this.active);
38155 * Gets the active {@link Roo.TabPanelItem}.
38156 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38158 getActiveTab : function(){
38159 return this.active;
38163 * Updates the tab body element to fit the height of the container element
38164 * for overflow scrolling
38165 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38167 syncHeight : function(targetHeight){
38168 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38169 var bm = this.bodyEl.getMargins();
38170 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38171 this.bodyEl.setHeight(newHeight);
38175 onResize : function(){
38176 if(this.monitorResize){
38177 this.autoSizeTabs();
38182 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38184 beginUpdate : function(){
38185 this.updating = true;
38189 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38191 endUpdate : function(){
38192 this.updating = false;
38193 this.autoSizeTabs();
38197 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38199 autoSizeTabs : function(){
38200 var count = this.items.length;
38201 var vcount = count - this.hiddenCount;
38202 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38205 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38206 var availWidth = Math.floor(w / vcount);
38207 var b = this.stripBody;
38208 if(b.getWidth() > w){
38209 var tabs = this.items;
38210 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38211 if(availWidth < this.minTabWidth){
38212 /*if(!this.sleft){ // incomplete scrolling code
38213 this.createScrollButtons();
38216 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38219 if(this.currentTabWidth < this.preferredTabWidth){
38220 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38226 * Returns the number of tabs in this TabPanel.
38229 getCount : function(){
38230 return this.items.length;
38234 * Resizes all the tabs to the passed width
38235 * @param {Number} The new width
38237 setTabWidth : function(width){
38238 this.currentTabWidth = width;
38239 for(var i = 0, len = this.items.length; i < len; i++) {
38240 if(!this.items[i].isHidden()) {
38241 this.items[i].setWidth(width);
38247 * Destroys this TabPanel
38248 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38250 destroy : function(removeEl){
38251 Roo.EventManager.removeResizeListener(this.onResize, this);
38252 for(var i = 0, len = this.items.length; i < len; i++){
38253 this.items[i].purgeListeners();
38255 if(removeEl === true){
38256 this.el.update("");
38261 createStrip : function(container)
38263 var strip = document.createElement("nav");
38264 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38265 container.appendChild(strip);
38269 createStripList : function(strip)
38271 // div wrapper for retard IE
38272 // returns the "tr" element.
38273 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38274 //'<div class="x-tabs-strip-wrap">'+
38275 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38276 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38277 return strip.firstChild; //.firstChild.firstChild.firstChild;
38279 createBody : function(container)
38281 var body = document.createElement("div");
38282 Roo.id(body, "tab-body");
38283 //Roo.fly(body).addClass("x-tabs-body");
38284 Roo.fly(body).addClass("tab-content");
38285 container.appendChild(body);
38288 createItemBody :function(bodyEl, id){
38289 var body = Roo.getDom(id);
38291 body = document.createElement("div");
38294 //Roo.fly(body).addClass("x-tabs-item-body");
38295 Roo.fly(body).addClass("tab-pane");
38296 bodyEl.insertBefore(body, bodyEl.firstChild);
38300 createStripElements : function(stripEl, text, closable, tpl)
38302 var td = document.createElement("li"); // was td..
38305 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38308 stripEl.appendChild(td);
38310 td.className = "x-tabs-closable";
38311 if(!this.closeTpl){
38312 this.closeTpl = new Roo.Template(
38313 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38314 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38315 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38318 var el = this.closeTpl.overwrite(td, {"text": text});
38319 var close = el.getElementsByTagName("div")[0];
38320 var inner = el.getElementsByTagName("em")[0];
38321 return {"el": el, "close": close, "inner": inner};
38324 // not sure what this is..
38325 // if(!this.tabTpl){
38326 //this.tabTpl = new Roo.Template(
38327 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38328 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38330 // this.tabTpl = new Roo.Template(
38331 // '<a href="#">' +
38332 // '<span unselectable="on"' +
38333 // (this.disableTooltips ? '' : ' title="{text}"') +
38334 // ' >{text}</span></a>'
38340 var template = tpl || this.tabTpl || false;
38344 template = new Roo.Template(
38346 '<span unselectable="on"' +
38347 (this.disableTooltips ? '' : ' title="{text}"') +
38348 ' >{text}</span></a>'
38352 switch (typeof(template)) {
38356 template = new Roo.Template(template);
38362 var el = template.overwrite(td, {"text": text});
38364 var inner = el.getElementsByTagName("span")[0];
38366 return {"el": el, "inner": inner};
38374 * @class Roo.TabPanelItem
38375 * @extends Roo.util.Observable
38376 * Represents an individual item (tab plus body) in a TabPanel.
38377 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38378 * @param {String} id The id of this TabPanelItem
38379 * @param {String} text The text for the tab of this TabPanelItem
38380 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38382 Roo.bootstrap.panel.TabItem = function(config){
38384 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38385 * @type Roo.TabPanel
38387 this.tabPanel = config.panel;
38389 * The id for this TabPanelItem
38392 this.id = config.id;
38394 this.disabled = false;
38396 this.text = config.text;
38398 this.loaded = false;
38399 this.closable = config.closable;
38402 * The body element for this TabPanelItem.
38403 * @type Roo.Element
38405 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38406 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38407 this.bodyEl.setStyle("display", "block");
38408 this.bodyEl.setStyle("zoom", "1");
38409 //this.hideAction();
38411 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38413 this.el = Roo.get(els.el);
38414 this.inner = Roo.get(els.inner, true);
38415 this.textEl = Roo.get(this.el.dom.firstChild, true);
38416 this.pnode = Roo.get(els.el.parentNode, true);
38417 // this.el.on("mousedown", this.onTabMouseDown, this);
38418 this.el.on("click", this.onTabClick, this);
38420 if(config.closable){
38421 var c = Roo.get(els.close, true);
38422 c.dom.title = this.closeText;
38423 c.addClassOnOver("close-over");
38424 c.on("click", this.closeClick, this);
38430 * Fires when this tab becomes the active tab.
38431 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38432 * @param {Roo.TabPanelItem} this
38436 * @event beforeclose
38437 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38438 * @param {Roo.TabPanelItem} this
38439 * @param {Object} e Set cancel to true on this object to cancel the close.
38441 "beforeclose": true,
38444 * Fires when this tab is closed.
38445 * @param {Roo.TabPanelItem} this
38449 * @event deactivate
38450 * Fires when this tab is no longer the active tab.
38451 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38452 * @param {Roo.TabPanelItem} this
38454 "deactivate" : true
38456 this.hidden = false;
38458 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38461 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38463 purgeListeners : function(){
38464 Roo.util.Observable.prototype.purgeListeners.call(this);
38465 this.el.removeAllListeners();
38468 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38471 this.pnode.addClass("active");
38474 this.tabPanel.stripWrap.repaint();
38476 this.fireEvent("activate", this.tabPanel, this);
38480 * Returns true if this tab is the active tab.
38481 * @return {Boolean}
38483 isActive : function(){
38484 return this.tabPanel.getActiveTab() == this;
38488 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38491 this.pnode.removeClass("active");
38493 this.fireEvent("deactivate", this.tabPanel, this);
38496 hideAction : function(){
38497 this.bodyEl.hide();
38498 this.bodyEl.setStyle("position", "absolute");
38499 this.bodyEl.setLeft("-20000px");
38500 this.bodyEl.setTop("-20000px");
38503 showAction : function(){
38504 this.bodyEl.setStyle("position", "relative");
38505 this.bodyEl.setTop("");
38506 this.bodyEl.setLeft("");
38507 this.bodyEl.show();
38511 * Set the tooltip for the tab.
38512 * @param {String} tooltip The tab's tooltip
38514 setTooltip : function(text){
38515 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38516 this.textEl.dom.qtip = text;
38517 this.textEl.dom.removeAttribute('title');
38519 this.textEl.dom.title = text;
38523 onTabClick : function(e){
38524 e.preventDefault();
38525 this.tabPanel.activate(this.id);
38528 onTabMouseDown : function(e){
38529 e.preventDefault();
38530 this.tabPanel.activate(this.id);
38533 getWidth : function(){
38534 return this.inner.getWidth();
38537 setWidth : function(width){
38538 var iwidth = width - this.pnode.getPadding("lr");
38539 this.inner.setWidth(iwidth);
38540 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38541 this.pnode.setWidth(width);
38545 * Show or hide the tab
38546 * @param {Boolean} hidden True to hide or false to show.
38548 setHidden : function(hidden){
38549 this.hidden = hidden;
38550 this.pnode.setStyle("display", hidden ? "none" : "");
38554 * Returns true if this tab is "hidden"
38555 * @return {Boolean}
38557 isHidden : function(){
38558 return this.hidden;
38562 * Returns the text for this tab
38565 getText : function(){
38569 autoSize : function(){
38570 //this.el.beginMeasure();
38571 this.textEl.setWidth(1);
38573 * #2804 [new] Tabs in Roojs
38574 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38576 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38577 //this.el.endMeasure();
38581 * Sets the text for the tab (Note: this also sets the tooltip text)
38582 * @param {String} text The tab's text and tooltip
38584 setText : function(text){
38586 this.textEl.update(text);
38587 this.setTooltip(text);
38588 //if(!this.tabPanel.resizeTabs){
38589 // this.autoSize();
38593 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38595 activate : function(){
38596 this.tabPanel.activate(this.id);
38600 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38602 disable : function(){
38603 if(this.tabPanel.active != this){
38604 this.disabled = true;
38605 this.pnode.addClass("disabled");
38610 * Enables this TabPanelItem if it was previously disabled.
38612 enable : function(){
38613 this.disabled = false;
38614 this.pnode.removeClass("disabled");
38618 * Sets the content for this TabPanelItem.
38619 * @param {String} content The content
38620 * @param {Boolean} loadScripts true to look for and load scripts
38622 setContent : function(content, loadScripts){
38623 this.bodyEl.update(content, loadScripts);
38627 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38628 * @return {Roo.UpdateManager} The UpdateManager
38630 getUpdateManager : function(){
38631 return this.bodyEl.getUpdateManager();
38635 * Set a URL to be used to load the content for this TabPanelItem.
38636 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38637 * @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)
38638 * @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)
38639 * @return {Roo.UpdateManager} The UpdateManager
38641 setUrl : function(url, params, loadOnce){
38642 if(this.refreshDelegate){
38643 this.un('activate', this.refreshDelegate);
38645 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38646 this.on("activate", this.refreshDelegate);
38647 return this.bodyEl.getUpdateManager();
38651 _handleRefresh : function(url, params, loadOnce){
38652 if(!loadOnce || !this.loaded){
38653 var updater = this.bodyEl.getUpdateManager();
38654 updater.update(url, params, this._setLoaded.createDelegate(this));
38659 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38660 * Will fail silently if the setUrl method has not been called.
38661 * This does not activate the panel, just updates its content.
38663 refresh : function(){
38664 if(this.refreshDelegate){
38665 this.loaded = false;
38666 this.refreshDelegate();
38671 _setLoaded : function(){
38672 this.loaded = true;
38676 closeClick : function(e){
38679 this.fireEvent("beforeclose", this, o);
38680 if(o.cancel !== true){
38681 this.tabPanel.removeTab(this.id);
38685 * The text displayed in the tooltip for the close icon.
38688 closeText : "Close this tab"
38691 * This script refer to:
38692 * Title: International Telephone Input
38693 * Author: Jack O'Connor
38694 * Code version: v12.1.12
38695 * Availability: https://github.com/jackocnr/intl-tel-input.git
38698 Roo.bootstrap.PhoneInputData = function() {
38701 "Afghanistan (افغانستان)",
38706 "Albania (Shqipëri)",
38711 "Algeria (الجزائر)",
38736 "Antigua and Barbuda",
38746 "Armenia (Հայաստան)",
38762 "Austria (Österreich)",
38767 "Azerbaijan (Azərbaycan)",
38777 "Bahrain (البحرين)",
38782 "Bangladesh (বাংলাদেশ)",
38792 "Belarus (Беларусь)",
38797 "Belgium (België)",
38827 "Bosnia and Herzegovina (Босна и Херцеговина)",
38842 "British Indian Ocean Territory",
38847 "British Virgin Islands",
38857 "Bulgaria (България)",
38867 "Burundi (Uburundi)",
38872 "Cambodia (កម្ពុជា)",
38877 "Cameroon (Cameroun)",
38886 ["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"]
38889 "Cape Verde (Kabu Verdi)",
38894 "Caribbean Netherlands",
38905 "Central African Republic (République centrafricaine)",
38925 "Christmas Island",
38931 "Cocos (Keeling) Islands",
38942 "Comoros (جزر القمر)",
38947 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38952 "Congo (Republic) (Congo-Brazzaville)",
38972 "Croatia (Hrvatska)",
38993 "Czech Republic (Česká republika)",
38998 "Denmark (Danmark)",
39013 "Dominican Republic (República Dominicana)",
39017 ["809", "829", "849"]
39035 "Equatorial Guinea (Guinea Ecuatorial)",
39055 "Falkland Islands (Islas Malvinas)",
39060 "Faroe Islands (Føroyar)",
39081 "French Guiana (Guyane française)",
39086 "French Polynesia (Polynésie française)",
39101 "Georgia (საქართველო)",
39106 "Germany (Deutschland)",
39126 "Greenland (Kalaallit Nunaat)",
39163 "Guinea-Bissau (Guiné Bissau)",
39188 "Hungary (Magyarország)",
39193 "Iceland (Ísland)",
39213 "Iraq (العراق)",
39229 "Israel (ישראל)",
39256 "Jordan (الأردن)",
39261 "Kazakhstan (Казахстан)",
39282 "Kuwait (الكويت)",
39287 "Kyrgyzstan (Кыргызстан)",
39297 "Latvia (Latvija)",
39302 "Lebanon (لبنان)",
39317 "Libya (ليبيا)",
39327 "Lithuania (Lietuva)",
39342 "Macedonia (FYROM) (Македонија)",
39347 "Madagascar (Madagasikara)",
39377 "Marshall Islands",
39387 "Mauritania (موريتانيا)",
39392 "Mauritius (Moris)",
39413 "Moldova (Republica Moldova)",
39423 "Mongolia (Монгол)",
39428 "Montenegro (Crna Gora)",
39438 "Morocco (المغرب)",
39444 "Mozambique (Moçambique)",
39449 "Myanmar (Burma) (မြန်မာ)",
39454 "Namibia (Namibië)",
39469 "Netherlands (Nederland)",
39474 "New Caledonia (Nouvelle-Calédonie)",
39509 "North Korea (조선 민주주의 인민 공화국)",
39514 "Northern Mariana Islands",
39530 "Pakistan (پاکستان)",
39540 "Palestine (فلسطين)",
39550 "Papua New Guinea",
39592 "Réunion (La Réunion)",
39598 "Romania (România)",
39614 "Saint Barthélemy",
39625 "Saint Kitts and Nevis",
39635 "Saint Martin (Saint-Martin (partie française))",
39641 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39646 "Saint Vincent and the Grenadines",
39661 "São Tomé and Príncipe (São Tomé e Príncipe)",
39666 "Saudi Arabia (المملكة العربية السعودية)",
39671 "Senegal (Sénégal)",
39701 "Slovakia (Slovensko)",
39706 "Slovenia (Slovenija)",
39716 "Somalia (Soomaaliya)",
39726 "South Korea (대한민국)",
39731 "South Sudan (جنوب السودان)",
39741 "Sri Lanka (ශ්රී ලංකාව)",
39746 "Sudan (السودان)",
39756 "Svalbard and Jan Mayen",
39767 "Sweden (Sverige)",
39772 "Switzerland (Schweiz)",
39777 "Syria (سوريا)",
39822 "Trinidad and Tobago",
39827 "Tunisia (تونس)",
39832 "Turkey (Türkiye)",
39842 "Turks and Caicos Islands",
39852 "U.S. Virgin Islands",
39862 "Ukraine (Україна)",
39867 "United Arab Emirates (الإمارات العربية المتحدة)",
39889 "Uzbekistan (Oʻzbekiston)",
39899 "Vatican City (Città del Vaticano)",
39910 "Vietnam (Việt Nam)",
39915 "Wallis and Futuna (Wallis-et-Futuna)",
39920 "Western Sahara (الصحراء الغربية)",
39926 "Yemen (اليمن)",
39950 * This script refer to:
39951 * Title: International Telephone Input
39952 * Author: Jack O'Connor
39953 * Code version: v12.1.12
39954 * Availability: https://github.com/jackocnr/intl-tel-input.git
39958 * @class Roo.bootstrap.PhoneInput
39959 * @extends Roo.bootstrap.TriggerField
39960 * An input with International dial-code selection
39962 * @cfg {String} defaultDialCode default '+852'
39963 * @cfg {Array} preferedCountries default []
39966 * Create a new PhoneInput.
39967 * @param {Object} config Configuration options
39970 Roo.bootstrap.PhoneInput = function(config) {
39971 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39974 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39976 listWidth: undefined,
39978 selectedClass: 'active',
39980 invalidClass : "has-warning",
39982 validClass: 'has-success',
39984 allowed: '0123456789',
39989 * @cfg {String} defaultDialCode The default dial code when initializing the input
39991 defaultDialCode: '+852',
39994 * @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
39996 preferedCountries: false,
39998 getAutoCreate : function()
40000 var data = Roo.bootstrap.PhoneInputData();
40001 var align = this.labelAlign || this.parentLabelAlign();
40004 this.allCountries = [];
40005 this.dialCodeMapping = [];
40007 for (var i = 0; i < data.length; i++) {
40009 this.allCountries[i] = {
40013 priority: c[3] || 0,
40014 areaCodes: c[4] || null
40016 this.dialCodeMapping[c[2]] = {
40019 priority: c[3] || 0,
40020 areaCodes: c[4] || null
40032 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40033 maxlength: this.max_length,
40034 cls : 'form-control tel-input',
40035 autocomplete: 'new-password'
40038 var hiddenInput = {
40041 cls: 'hidden-tel-input'
40045 hiddenInput.name = this.name;
40048 if (this.disabled) {
40049 input.disabled = true;
40052 var flag_container = {
40069 cls: this.hasFeedback ? 'has-feedback' : '',
40075 cls: 'dial-code-holder',
40082 cls: 'roo-select2-container input-group',
40089 if (this.fieldLabel.length) {
40092 tooltip: 'This field is required'
40098 cls: 'control-label',
40104 html: this.fieldLabel
40107 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40113 if(this.indicatorpos == 'right') {
40114 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40121 if(align == 'left') {
40129 if(this.labelWidth > 12){
40130 label.style = "width: " + this.labelWidth + 'px';
40132 if(this.labelWidth < 13 && this.labelmd == 0){
40133 this.labelmd = this.labelWidth;
40135 if(this.labellg > 0){
40136 label.cls += ' col-lg-' + this.labellg;
40137 input.cls += ' col-lg-' + (12 - this.labellg);
40139 if(this.labelmd > 0){
40140 label.cls += ' col-md-' + this.labelmd;
40141 container.cls += ' col-md-' + (12 - this.labelmd);
40143 if(this.labelsm > 0){
40144 label.cls += ' col-sm-' + this.labelsm;
40145 container.cls += ' col-sm-' + (12 - this.labelsm);
40147 if(this.labelxs > 0){
40148 label.cls += ' col-xs-' + this.labelxs;
40149 container.cls += ' col-xs-' + (12 - this.labelxs);
40159 var settings = this;
40161 ['xs','sm','md','lg'].map(function(size){
40162 if (settings[size]) {
40163 cfg.cls += ' col-' + size + '-' + settings[size];
40167 this.store = new Roo.data.Store({
40168 proxy : new Roo.data.MemoryProxy({}),
40169 reader : new Roo.data.JsonReader({
40180 'name' : 'dialCode',
40184 'name' : 'priority',
40188 'name' : 'areaCodes',
40195 if(!this.preferedCountries) {
40196 this.preferedCountries = [
40203 var p = this.preferedCountries.reverse();
40206 for (var i = 0; i < p.length; i++) {
40207 for (var j = 0; j < this.allCountries.length; j++) {
40208 if(this.allCountries[j].iso2 == p[i]) {
40209 var t = this.allCountries[j];
40210 this.allCountries.splice(j,1);
40211 this.allCountries.unshift(t);
40217 this.store.proxy.data = {
40219 data: this.allCountries
40225 initEvents : function()
40228 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40230 this.indicator = this.indicatorEl();
40231 this.flag = this.flagEl();
40232 this.dialCodeHolder = this.dialCodeHolderEl();
40234 this.trigger = this.el.select('div.flag-box',true).first();
40235 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40240 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40241 _this.list.setWidth(lw);
40244 this.list.on('mouseover', this.onViewOver, this);
40245 this.list.on('mousemove', this.onViewMove, this);
40246 this.inputEl().on("keyup", this.onKeyUp, this);
40247 this.inputEl().on("keypress", this.onKeyPress, this);
40249 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40251 this.view = new Roo.View(this.list, this.tpl, {
40252 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40255 this.view.on('click', this.onViewClick, this);
40256 this.setValue(this.defaultDialCode);
40259 onTriggerClick : function(e)
40261 Roo.log('trigger click');
40266 if(this.isExpanded()){
40268 this.hasFocus = false;
40270 this.store.load({});
40271 this.hasFocus = true;
40276 isExpanded : function()
40278 return this.list.isVisible();
40281 collapse : function()
40283 if(!this.isExpanded()){
40287 Roo.get(document).un('mousedown', this.collapseIf, this);
40288 Roo.get(document).un('mousewheel', this.collapseIf, this);
40289 this.fireEvent('collapse', this);
40293 expand : function()
40297 if(this.isExpanded() || !this.hasFocus){
40301 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40302 this.list.setWidth(lw);
40305 this.restrictHeight();
40307 Roo.get(document).on('mousedown', this.collapseIf, this);
40308 Roo.get(document).on('mousewheel', this.collapseIf, this);
40310 this.fireEvent('expand', this);
40313 restrictHeight : function()
40315 this.list.alignTo(this.inputEl(), this.listAlign);
40316 this.list.alignTo(this.inputEl(), this.listAlign);
40319 onViewOver : function(e, t)
40321 if(this.inKeyMode){
40324 var item = this.view.findItemFromChild(t);
40327 var index = this.view.indexOf(item);
40328 this.select(index, false);
40333 onViewClick : function(view, doFocus, el, e)
40335 var index = this.view.getSelectedIndexes()[0];
40337 var r = this.store.getAt(index);
40340 this.onSelect(r, index);
40342 if(doFocus !== false && !this.blockFocus){
40343 this.inputEl().focus();
40347 onViewMove : function(e, t)
40349 this.inKeyMode = false;
40352 select : function(index, scrollIntoView)
40354 this.selectedIndex = index;
40355 this.view.select(index);
40356 if(scrollIntoView !== false){
40357 var el = this.view.getNode(index);
40359 this.list.scrollChildIntoView(el, false);
40364 createList : function()
40366 this.list = Roo.get(document.body).createChild({
40368 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40369 style: 'display:none'
40372 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40375 collapseIf : function(e)
40377 var in_combo = e.within(this.el);
40378 var in_list = e.within(this.list);
40379 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40381 if (in_combo || in_list || is_list) {
40387 onSelect : function(record, index)
40389 if(this.fireEvent('beforeselect', this, record, index) !== false){
40391 this.setFlagClass(record.data.iso2);
40392 this.setDialCode(record.data.dialCode);
40393 this.hasFocus = false;
40395 this.fireEvent('select', this, record, index);
40399 flagEl : function()
40401 var flag = this.el.select('div.flag',true).first();
40408 dialCodeHolderEl : function()
40410 var d = this.el.select('input.dial-code-holder',true).first();
40417 setDialCode : function(v)
40419 this.dialCodeHolder.dom.value = '+'+v;
40422 setFlagClass : function(n)
40424 this.flag.dom.className = 'flag '+n;
40427 getValue : function()
40429 var v = this.inputEl().getValue();
40430 if(this.dialCodeHolder) {
40431 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40436 setValue : function(v)
40438 var d = this.getDialCode(v);
40440 //invalid dial code
40441 if(v.length == 0 || !d || d.length == 0) {
40443 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40444 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40450 this.setFlagClass(this.dialCodeMapping[d].iso2);
40451 this.setDialCode(d);
40452 this.inputEl().dom.value = v.replace('+'+d,'');
40453 this.hiddenEl().dom.value = this.getValue();
40458 getDialCode : function(v)
40462 if (v.length == 0) {
40463 return this.dialCodeHolder.dom.value;
40467 if (v.charAt(0) != "+") {
40470 var numericChars = "";
40471 for (var i = 1; i < v.length; i++) {
40472 var c = v.charAt(i);
40475 if (this.dialCodeMapping[numericChars]) {
40476 dialCode = v.substr(1, i);
40478 if (numericChars.length == 4) {
40488 this.setValue(this.defaultDialCode);
40492 hiddenEl : function()
40494 return this.el.select('input.hidden-tel-input',true).first();
40497 // after setting val
40498 onKeyUp : function(e){
40499 this.setValue(this.getValue());
40502 onKeyPress : function(e){
40503 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40510 * @class Roo.bootstrap.MoneyField
40511 * @extends Roo.bootstrap.ComboBox
40512 * Bootstrap MoneyField class
40515 * Create a new MoneyField.
40516 * @param {Object} config Configuration options
40519 Roo.bootstrap.MoneyField = function(config) {
40521 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40525 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40528 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40530 allowDecimals : true,
40532 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40534 decimalSeparator : ".",
40536 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40538 decimalPrecision : 0,
40540 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40542 allowNegative : true,
40544 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40548 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40550 minValue : Number.NEGATIVE_INFINITY,
40552 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40554 maxValue : Number.MAX_VALUE,
40556 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40558 minText : "The minimum value for this field is {0}",
40560 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40562 maxText : "The maximum value for this field is {0}",
40564 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40565 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40567 nanText : "{0} is not a valid number",
40569 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40573 * @cfg {String} defaults currency of the MoneyField
40574 * value should be in lkey
40576 defaultCurrency : false,
40578 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40580 thousandsDelimiter : false,
40582 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40593 getAutoCreate : function()
40595 var align = this.labelAlign || this.parentLabelAlign();
40607 cls : 'form-control roo-money-amount-input',
40608 autocomplete: 'new-password'
40611 var hiddenInput = {
40615 cls: 'hidden-number-input'
40618 if(this.max_length) {
40619 input.maxlength = this.max_length;
40623 hiddenInput.name = this.name;
40626 if (this.disabled) {
40627 input.disabled = true;
40630 var clg = 12 - this.inputlg;
40631 var cmd = 12 - this.inputmd;
40632 var csm = 12 - this.inputsm;
40633 var cxs = 12 - this.inputxs;
40637 cls : 'row roo-money-field',
40641 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40645 cls: 'roo-select2-container input-group',
40649 cls : 'form-control roo-money-currency-input',
40650 autocomplete: 'new-password',
40652 name : this.currencyName
40656 cls : 'input-group-addon',
40670 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40674 cls: this.hasFeedback ? 'has-feedback' : '',
40685 if (this.fieldLabel.length) {
40688 tooltip: 'This field is required'
40694 cls: 'control-label',
40700 html: this.fieldLabel
40703 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40709 if(this.indicatorpos == 'right') {
40710 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40717 if(align == 'left') {
40725 if(this.labelWidth > 12){
40726 label.style = "width: " + this.labelWidth + 'px';
40728 if(this.labelWidth < 13 && this.labelmd == 0){
40729 this.labelmd = this.labelWidth;
40731 if(this.labellg > 0){
40732 label.cls += ' col-lg-' + this.labellg;
40733 input.cls += ' col-lg-' + (12 - this.labellg);
40735 if(this.labelmd > 0){
40736 label.cls += ' col-md-' + this.labelmd;
40737 container.cls += ' col-md-' + (12 - this.labelmd);
40739 if(this.labelsm > 0){
40740 label.cls += ' col-sm-' + this.labelsm;
40741 container.cls += ' col-sm-' + (12 - this.labelsm);
40743 if(this.labelxs > 0){
40744 label.cls += ' col-xs-' + this.labelxs;
40745 container.cls += ' col-xs-' + (12 - this.labelxs);
40756 var settings = this;
40758 ['xs','sm','md','lg'].map(function(size){
40759 if (settings[size]) {
40760 cfg.cls += ' col-' + size + '-' + settings[size];
40767 initEvents : function()
40769 this.indicator = this.indicatorEl();
40771 this.initCurrencyEvent();
40773 this.initNumberEvent();
40776 initCurrencyEvent : function()
40779 throw "can not find store for combo";
40782 this.store = Roo.factory(this.store, Roo.data);
40783 this.store.parent = this;
40787 this.triggerEl = this.el.select('.input-group-addon', true).first();
40789 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40794 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40795 _this.list.setWidth(lw);
40798 this.list.on('mouseover', this.onViewOver, this);
40799 this.list.on('mousemove', this.onViewMove, this);
40800 this.list.on('scroll', this.onViewScroll, this);
40803 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40806 this.view = new Roo.View(this.list, this.tpl, {
40807 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40810 this.view.on('click', this.onViewClick, this);
40812 this.store.on('beforeload', this.onBeforeLoad, this);
40813 this.store.on('load', this.onLoad, this);
40814 this.store.on('loadexception', this.onLoadException, this);
40816 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40817 "up" : function(e){
40818 this.inKeyMode = true;
40822 "down" : function(e){
40823 if(!this.isExpanded()){
40824 this.onTriggerClick();
40826 this.inKeyMode = true;
40831 "enter" : function(e){
40834 if(this.fireEvent("specialkey", this, e)){
40835 this.onViewClick(false);
40841 "esc" : function(e){
40845 "tab" : function(e){
40848 if(this.fireEvent("specialkey", this, e)){
40849 this.onViewClick(false);
40857 doRelay : function(foo, bar, hname){
40858 if(hname == 'down' || this.scope.isExpanded()){
40859 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40867 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40871 initNumberEvent : function(e)
40873 this.inputEl().on("keydown" , this.fireKey, this);
40874 this.inputEl().on("focus", this.onFocus, this);
40875 this.inputEl().on("blur", this.onBlur, this);
40877 this.inputEl().relayEvent('keyup', this);
40879 if(this.indicator){
40880 this.indicator.addClass('invisible');
40883 this.originalValue = this.getValue();
40885 if(this.validationEvent == 'keyup'){
40886 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40887 this.inputEl().on('keyup', this.filterValidation, this);
40889 else if(this.validationEvent !== false){
40890 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40893 if(this.selectOnFocus){
40894 this.on("focus", this.preFocus, this);
40897 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40898 this.inputEl().on("keypress", this.filterKeys, this);
40900 this.inputEl().relayEvent('keypress', this);
40903 var allowed = "0123456789";
40905 if(this.allowDecimals){
40906 allowed += this.decimalSeparator;
40909 if(this.allowNegative){
40913 if(this.thousandsDelimiter) {
40917 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40919 var keyPress = function(e){
40921 var k = e.getKey();
40923 var c = e.getCharCode();
40926 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40927 allowed.indexOf(String.fromCharCode(c)) === -1
40933 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40937 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40942 this.inputEl().on("keypress", keyPress, this);
40946 onTriggerClick : function(e)
40953 this.loadNext = false;
40955 if(this.isExpanded()){
40960 this.hasFocus = true;
40962 if(this.triggerAction == 'all') {
40963 this.doQuery(this.allQuery, true);
40967 this.doQuery(this.getRawValue());
40970 getCurrency : function()
40972 var v = this.currencyEl().getValue();
40977 restrictHeight : function()
40979 this.list.alignTo(this.currencyEl(), this.listAlign);
40980 this.list.alignTo(this.currencyEl(), this.listAlign);
40983 onViewClick : function(view, doFocus, el, e)
40985 var index = this.view.getSelectedIndexes()[0];
40987 var r = this.store.getAt(index);
40990 this.onSelect(r, index);
40994 onSelect : function(record, index){
40996 if(this.fireEvent('beforeselect', this, record, index) !== false){
40998 this.setFromCurrencyData(index > -1 ? record.data : false);
41002 this.fireEvent('select', this, record, index);
41006 setFromCurrencyData : function(o)
41010 this.lastCurrency = o;
41012 if (this.currencyField) {
41013 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41015 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41018 this.lastSelectionText = currency;
41020 //setting default currency
41021 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41022 this.setCurrency(this.defaultCurrency);
41026 this.setCurrency(currency);
41029 setFromData : function(o)
41033 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41035 this.setFromCurrencyData(c);
41040 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41042 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41045 this.setValue(value);
41049 setCurrency : function(v)
41051 this.currencyValue = v;
41054 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41059 setValue : function(v)
41061 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41067 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41069 this.inputEl().dom.value = (v == '') ? '' :
41070 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41072 if(!this.allowZero && v === '0') {
41073 this.hiddenEl().dom.value = '';
41074 this.inputEl().dom.value = '';
41081 getRawValue : function()
41083 var v = this.inputEl().getValue();
41088 getValue : function()
41090 return this.fixPrecision(this.parseValue(this.getRawValue()));
41093 parseValue : function(value)
41095 if(this.thousandsDelimiter) {
41097 r = new RegExp(",", "g");
41098 value = value.replace(r, "");
41101 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41102 return isNaN(value) ? '' : value;
41106 fixPrecision : function(value)
41108 if(this.thousandsDelimiter) {
41110 r = new RegExp(",", "g");
41111 value = value.replace(r, "");
41114 var nan = isNaN(value);
41116 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41117 return nan ? '' : value;
41119 return parseFloat(value).toFixed(this.decimalPrecision);
41122 decimalPrecisionFcn : function(v)
41124 return Math.floor(v);
41127 validateValue : function(value)
41129 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41133 var num = this.parseValue(value);
41136 this.markInvalid(String.format(this.nanText, value));
41140 if(num < this.minValue){
41141 this.markInvalid(String.format(this.minText, this.minValue));
41145 if(num > this.maxValue){
41146 this.markInvalid(String.format(this.maxText, this.maxValue));
41153 validate : function()
41155 if(this.disabled || this.allowBlank){
41160 var currency = this.getCurrency();
41162 if(this.validateValue(this.getRawValue()) && currency.length){
41167 this.markInvalid();
41171 getName: function()
41176 beforeBlur : function()
41182 var v = this.parseValue(this.getRawValue());
41189 onBlur : function()
41193 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41194 //this.el.removeClass(this.focusClass);
41197 this.hasFocus = false;
41199 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41203 var v = this.getValue();
41205 if(String(v) !== String(this.startValue)){
41206 this.fireEvent('change', this, v, this.startValue);
41209 this.fireEvent("blur", this);
41212 inputEl : function()
41214 return this.el.select('.roo-money-amount-input', true).first();
41217 currencyEl : function()
41219 return this.el.select('.roo-money-currency-input', true).first();
41222 hiddenEl : function()
41224 return this.el.select('input.hidden-number-input',true).first();